Activiti and candidate groups - alfresco

In APS 1.8.1, I have defined a process where each task has a candidate group.
When I login in with a user that belongs to a candidate group, I cannot see the process instance.
I have found out that when I try to access the process instances, APS executes the following query in the database:
select distinct RES.* , DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_
from ACT_HI_PROCINST RES
left outer join ACT_RE_PROCDEF DEF on RES.PROC_DEF_ID_ = DEF.ID_
left join ACT_HI_IDENTITYLINK I_OR0 on I_OR0.PROC_INST_ID_ = RES.ID_
WHERE RES.TENANT_ID_ = 'tenant_1'
and
( (
exists(select LINK.USER_ID_ from ACT_HI_IDENTITYLINK LINK where USER_ID_ = '1003' and LINK.PROC_INST_ID_ = RES.ID_)
)
or (
I_OR0.TYPE_ = 'participant'
and
I_OR0.GROUP_ID_ IN ('1','2','2023','2013','2024','2009','2025','2026','2027','2028','2029','2007','2018','2020','2017','2015','2012','2003','2021','2019','2004','2002','2005','2030','2031','2032','2011','2006','2008','2014','2010','2016','2022','2033','2034','2035','2036','2037','1003')
) )
order by RES.START_TIME_ desc
LIMIT 50 OFFSET 0
This query does not return any record for two reasons:
In my ACT_HI_IDENTITYLINK no tasks have both the group_id_ and the proc_inst_id_ set.
The type of the record is "candidate" but the query is looking for "participant"
select * fro m ACT_HI_IDENTITYLINK;
-[ RECORD 1 ]-+----------
id_ | 260228
group_id_ |
type_ | starter
user_id_ | 1002
task_id_ |
proc_inst_id_ | 260226
-[ RECORD 2 ]-+----------
id_ | 260294
group_id_ | 2006
type_ | candidate
user_id_ |
task_id_ | 260293
proc_inst_id_ |
-[ RECORD 3 ]-+----------
id_ | 260300
group_id_ | 2009
type_ | candidate
user_id_ |
task_id_ | 260299
proc_inst_id_ |
-[ RECORD 4 ]-+----------
id_ | 262503
group_id_ |
type_ | starter
user_id_ | 1002
task_id_ |
proc_inst_id_ | 262501
-[ RECORD 5 ]-+----------
id_ | 262569
group_id_ | 2016
type_ | candidate
user_id_ |
task_id_ | 262568
proc_inst_id_ |
-[ RECORD 6 ]-+----------
id_ | 262575
group_id_ | 2027
type_ | candidate
user_id_ |
task_id_ | 262574
proc_inst_id_ |
Why the query is looking only for "participant" and why the records that have type_ = 'candidate' do not have any proc_inst_id_ set ?
UPDATE:
The problem with the constraint "participant" has a simple workaround: it would be enough to add the same candidate group as a participant.
See also Feature allowing "Participant" configuration in BPM Modeler
Unfortunately this is not enough to solve the second problem. The record is still not returned because the column proc_inst_id_ is not set.
I tried to update the column manually on the "participant" record and I have verified that doing so the page is accessible and works well.
Does anyone know why the column is not set ?

A possible solution (or workaround until ACTIVITI-696 is fixed) is to add each group added as candidate to a task as a participant of the process instance.
There is a REST API that does it:
POST /enterprise/process-instances/{processInstanceId}/identitylinks
What this API does should be done by a task listener that will automatically add the candidate groups of the created task as participant of the process instance.
To add the new identity link, use in the listener the following lines:
ActivitiEntityEvent aee = (ActivitiEntityEvent)activitiEvent;
TaskEntity taskEntity = (TaskEntity)aee.getEntity();
List<IdentityLinkEntity> identities = taskEntity.getIdentityLinks();
if (identities != null) {
for (IdentityLinkEntity identityLinkEntity : identities) {
String groupId = identityLinkEntity.getGroupId();
runtimeService.addGroupIdentityLink(activitiEvent.getProcessInstanceId(), groupId, "participant");
};
}

first try to check that your workflow is really started by access to "workflow I have started". You should see your task in "active task" if not, that means there is some errors in your definitions. If everything is ok, check your group name and don’t forget to add "GROUP_"myGRPName.
If you want to see the workflow instances it’s simpler with web script and services.

Related

How is availability zone list order determined by the nova api in openstack?

I want to change the default option for availability zone in my openstack setup in horizon. However, I am having trouble finding out what determines the order of the availability zones as returned by the nova api. For example, running openstack availability zone list I get:
+--------------+-------------+
| Zone Name | Zone Status |
+--------------+-------------+
| zone2 | available |
| zone1 | available |
| internal | available |
| zone3 | available |
+--------------+-------------+
which is the same order as in horizon's dropdown box. However, querying the database directly, I get:
mysql> select * from aggregate_metadata;
+---------------------+------------+------------+----+--------------+-------------------+--------------+---------+
| created_at | updated_at | deleted_at | id | aggregate_id | key | value | deleted |
+---------------------+------------+------------+----+--------------+-------------------+--------------+---------+
| 2015-06-12 08:43:07 | NULL | NULL | 1 | 1 | availability_zone | zone1 | 0 |
| 2015-06-12 08:43:08 | NULL | NULL | 2 | 2 | availability_zone | zone2 | 0 |
| 2015-10-26 05:30:15 | NULL | NULL | 3 | 3 | availability_zone | zone3 | 0 |
+---------------------+------------+------------+----+--------------+-------------------+--------------+---------+
3 rows in set (0.00 sec)
Obviously, the openstack api is doing some sorting before returning the result... however, I can't figure out how it is being sorted nor how I could control the sorting.
get_availability_zones is the function used by nova api to collect list of availability zones.
This function gets list of available services(which is sorted based on the id) ,adds availability zone name is added to those services.
Since service list is the first step it's id defines the order and not the zone name.
The sort order can be modified in different ways based on the requirement.
Sort the order at frontend (horizon)
Modify this line with
ng-options="zone.value as zone.label for zone in model.availabilityZones | orderBy:'value'"
Sort the order at backend (nova-api)
Add available_zones.sort()not_available_zones.sort() before return statements in get_availability_zones function

Symfony2 dynamic form builder

I want to create a bundle in an application where the user has the ability to create forms.
These forms will be questionnaires with different answers. Do you know if there is already a similar bundle exists?
If it does not exist. How should I proceed? I always create the forms only on files like "Form\Type\UserType".
In this case, would have to be generated dynamically from the database, right? Only I miss the approach, can anyone give a hint on how I can realize this?
Update:
Maybe i think to complicated. I'm not sure if an form service solve my problem. Know i created a database structure to describe my initial situation.
The user can create on a backend following records.
A scale, a scale can contain many answers (yes, no, maybe, good, better ...)
A question category
A question, can be assigned to many categories and many questionnairies
A questionnaire, can contain many questions and here can the user assign a scale to a question.
Table Scale
+----+--------+------------+----------+-----------+------------+
| id | name | alignment | isActive | isDeleted | createDate |
+----+--------+------------+----------+-----------+------------+
| 1 | Yes-No | horizontal | 1 | 1 | 2014-09-25 |
+----+--------+------------+----------+-----------+------------+
Table Items
+----+------+-------+------------+
| id | name | value | createDate |
+----+------+-------+------------+
| 1 | Yes | 1 | 2014-09-25 |
| 2 | No | 0 | 2014-09-25 |
+----+------+-------+------------+
ManyToMany 'scale_items'
+----------+----------+
| scale_id | items_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
+----------+----------+
Table category for question categories
+----+---------+----------+-----------+------------+
| id | name | isActive | isDeleted | createDate |
+----+---------+----------+-----------+------------+
| 1 | General | 1 | 0 | 2014-09-25 |
+----+---------+----------+-----------+------------+
Table question
+----+-----------------------------------------+------------+
| id | question | createDate |
+----+-----------------------------------------+------------+
| 1 | Are you satisfied with the cleanliness? | 2014-09-25 |
+----+-----------------------------------------+------------+
ManyToMany 'question_category'
+-------------+-------------+
| question_id | category_id |
+-------------+-------------+
| 1 | 1 |
+-------------+-------------+
Table questionnaire
+----+-------------------+---------+----------+-----------+------------+
| id | name | version | isActive | isDeleted | createDate |
+----+-------------------+---------+----------+-----------+------------+
| 1 | General Questions | 2.2 | 1 | 0 | 2014-09-25 |
+----+-------------------+---------+----------+-----------+------------+
Now the database contain scales and items, questions and categories and a table for the questionnaire. Now i created a big relation to assign questions to questionnairies with a specified scale. A question can be assigned to different scales on different questionaries.
Table questionnaire_question_scale
+----+-------------+------------------+----------+------+--------+
| id | question_id | questionnaire_id | scale_id | page | hash |
+----+-------------+------------------+----------+------+--------+
| 1 | 1 | 1 | 1 | 1 | X321Z1 |
+----+-------------+------------------+----------+------+--------+
In the final step i create a relation table to assign a questionnaire to a couple of users.
Table questionnaire_user
+---------+------------------+
| user_id | questionnaire_id |
+---------+------------------+
| 21 | 1 |
+---------+------------------+
Now if the user log in i will render the above informations as form and here starts my problem :)
I think my solution was inefficient, because if many users log in to fill out a questionnaire i must generate every time the questionnaire (the complex structure) as form.
This is a end point for me, unfortunately I do not know further.
I would be very grateful for ideas, tips and solution approaches
If you want to build it yourself: I suggest creating a form type, declaring it as a service and injecting the form repository (explained further down) in it.For storing it in the data base: you can create two entities (actually creating a seperate entity for each field seems better but for the sake of simplicity I use two entities) : one for forms : YourBundle:Form and one for your form-fields YourBundle:FormField.the Form entity can only contain an id and a name and a one-to-many association to FormField. the data you store in the FormField will be: a many-to-one association to Form - the field name - the field type - the field's options.you can store the options as Json or other formats and later decode it.

SQLite3: dynamic between query

I have this sqlite3 table (simplified):
+--------+----------+-------+
| ROUTE | WPNumber | WPID |
+--------+----------+-------+
| A123 | 1 | WP001 |
| A123 | 2 | WP002 |
| A123 | 3 | WP003 |
| [...] | [...] | [...] |
| A123 | 20 | WP020 |
+--------+----------+-------+
Lets say I want to travel this route in the reverse direction (020 to 001).
How do I get all the WPID's in between? I know it is possible to build a query using BETWEEN and DESC, but then I'd have to build two seperate queries and have Python check when to use which query. Is it possible to have sqlite3 do the work, independent of the direction (reverse or not).
You can reverse the sorting order by reversing the number used in the ORDER BY clause.
Set the parameter ? to either 1 or -1:
SELECT WPID
FROM ThisTable
WHERE ROUTE = 'A123'
ORDER BY WPNumber * ?
If you would just use a similar query with DESC, the database would have a better opportunity to optimize the sorting with an index.

oracle 11g execution plan behaviour

I have this very simple query performing filtering and join by rowid.
SELECT *
FROM BOOKING.BOOKING_GRID BG,
BOOKING.BOOKING_STATES BS
WHERE BG.hotel=128
AND BS.ROWID =BG.BOOKINGSTATE;
when I explain plan I get:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1597031677
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6137K| 1041M| | 1763K (1)| 05:48:27 |
|* 1 | HASH JOIN | | 6137K| 1041M| 538M| 1763K (1)| 05:48:27 |
|* 2 | INDEX UNIQUE SCAN| BOOKING_GRIDPK | 6137K| 468M| | 547K (1)| 01:48:05 |
|* 3 | INDEX RANGE SCAN| BOOKING_GRID_INDEX5 | 6137K| | | 90388 (1)| 00:17:52 |
| 4 | TABLE ACCESS FULL| BOOKING_STATES | 158M| 14G| | 365K (2)| 01:12:14 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("BS".ROWID="BG"."BOOKINGSTATE")
2 - access("BG"."HOTEL"=128)
3 - access("BG"."HOTEL"=128)
Index for BOOKING_GRID are:
BOOKING BOOKING_GRIDPK UNIQUE VALID IOT - TOP N NO NO HOTEL, DAY, BOOKINGSTATE
BOOKING BOOKING_GRID_UNIQ UNIQUE VALID NORMAL N NO NO HOTEL, DAY, BOOKING, VALIDITYSTART
BOOKING BOOKING_GRID_INDEX5 NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKINGSTATUS, ISDAYUSE, DAY
BOOKING BOOKING_GRID_INDEX7 NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKING, VALIDITYSTART
BOOKING BOOKING_GRID_INDEX10 NONUNIQUE VALID NORMAL N NO NO HOTEL, ISDAYUSE, BOOKINGSTATUS, DAY
Index for BOOKING_STATES are:
BOOKING BOOKING_STATES_PK UNIQUE VALID NORMAL N NO NO HOTEL, BOOKING, VALIDITYSTART
BOOKING BOOKING_STATES_INDEX2 NONUNIQUE VALID NORMAL N NO NO HOTEL, YIELDROOMTYPE, BOOKEDROOMTYPE, ROOMTYPE
BOOKING BOOKING_STATES_BOOKING NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKING, BOOKINGSTATUS
BOOKING BOOKING_NOSEGMENT_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00034$ TO_NUMBER(DECODE(TO_CHAR("MARKETSEGMENT"),NULL,DECODE("BOOK",0,NULL,TO_CHAR(DECODE("ISDAYUSE",'N',DECODE("ISSHARED",'N',DECODE("BOOKINGSTATUS",'B',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'I',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'W',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"))))))))
BOOKING BOOKING_NORATE_CODE_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00033$ TO_NUMBER(DECODE(TO_CHAR("RATECODE"),NULL,DECODE("BOOK",0,NULL,TO_CHAR(DECODE("ISDAYUSE",'N',DECODE("ISSHARED",'N',DECODE("BOOKINGSTATUS",'B',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'I',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'W',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"))))))))
BOOKING BOOKING_NOBOOKINGTYPE_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00032$ TO_NUMBER(DECODE(TO_CHAR("BOOKINGTYPE"),NULL,DECODE("BOOK",0,NULL,TO_CHAR(DECODE("ISDAYUSE",'N',DECODE("ISSHARED",'N',DECODE("BOOKINGSTATUS",'B',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"))))))))
BOOKING BOOKING_STATES_BOOKING_TYPE NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKINGTYPE, ISDAYUSE, BOOKINGSTATUS
BOOKING BOOKING_STATES_CANCEL_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00035$, SYS_NC00036$ DECODE("BOOKINGSTATUS",'c',"HOTEL",'C',"HOTEL")
BOOKING BOOKING_STATES_CANCEL_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00035$, SYS_NC00036$ DECODE("BOOKINGSTATUS",'c',"CANCELREASON",'C',"CANCELREASON")
I don't understand two things:
why Oracle would decide it is faster to join before filtering on hotel=128?
why using a hash join when it could use a "TABLE ACCESS BY USER ROWID"
The weird thing is that when I run the same exact request with hotel=201, it is perfectly fine:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4251203092
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 591K| 100M| 643K (1)| 02:07:12 |
| 1 | NESTED LOOPS | | 591K| 100M| 643K (1)| 02:07:12 |
|* 2 | INDEX UNIQUE SCAN | BOOKING_GRIDPK | 591K| 45M| 52686 (1)| 00:10:25 |
|* 3 | INDEX RANGE SCAN | BOOKING_GRID_INDEX5 | 591K| | 8707 (1)| 00:01:44 |
| 4 | TABLE ACCESS BY USER ROWID| BOOKING_STATES | 1 | 98 | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("BG"."HOTEL"=201)
3 - access("BG"."HOTEL"=201)
Any idea about what's going on there?
Thank you,
Renaud
The reason for the different execution paths is because Oracle thinks that there are about 6 million rows with hotel=128 but only 591,000 with hotel=201. In the case of the bigger intermediate set, Oracle chose a hash join over nested loops.
What I don't get is this:
AND BS.ROWID =BG.BOOKINGSTATE;
You're storing Oracle-format ROWIDs in a column called BOOKINGSTATE???
Ok, given your confirmation that BOOKINGSTATE really does contain Oracle ROWIDs, here's why I'd say you're getting a HASH JOIN instead of a NESTED LOOP join:
First, when Oracle reads a row, it doesn't just read a row at a time, it reads blocks at a time. So to do a NESTED LOOP join with a TABLE ACCESS BY USER ROWID lookup, it's going to find a row in BOOKING_GRID and then go read the block that has the row in BOOKING_STATES with that ROWID. The catch is, if later on there's another row with a rowid in a block that it's already read before, it's going to re-read that block (sure, it may be cached) to get the other row. Kind of like "open the block, get a row, close the box....then later on open the same box again, get another row, close the box, move on to the next box"
On the other hand, your hash join is:
- sorting the rows in the smaller set (in this case the rows in BOOKING_GRID where hotel=128), put them in memory
- full table scan BOOKING_STATES - and here's the kicker - using multiblock reads. it reads many blocks at a time and processes all of the rows in a block without needing to re-read it later. It's like "open the box, process all of the rows in the box, then close the box."
(For more details on the above check out http://docs.oracle.com/cd/B28359_01/server.111/b28274/optimops.htm, in particular the following sections:
11.5.1 Full Table Scans
11.5.3.1 Assessing I/O for Blocks, not Rows
11.6.3 Nested Loop Joins
11.6.4 Hash Joins
)
By the way, it's a bit curious that it's doing the "access("BG"."HOTEL"=128)" step twice using two indexes -how are the BOOKING_GRIDPK and BOOKING_GRID_INDEX5 indexes defined? You're asking for all columns from both tables, but the plan never touches the BOOKING_GRID table.)

Sumtotal in ReportViewer

+----------+------------+------+------+--------------+---------+---------+
| | SUBJ | MIN | MAX | RESULT | STATUS | PERCENT |
| +------------+------+------+--------------+---------+---------+
| | Subj1 | 35 | 100 | 13 | FAIL | 13.00% |
|EXAM NAME | Subj2 | 35 | 100 | 63 | PASS | 63.00% |
| | Subj3 | 35 | 100 | 35 | PASS | 35.00% |
| +------------+------+------+--------------+---------+---------+
| | Total | 105 | 300 | 111 | PASS | 37.00% |
+----------+------------+------+------+--------------+---------+---------+
This is my report viewer report format.The SubTotal row counts the
total of all the above column.Every thing is fine. But in the status
column its showing Pass. I want it to show fail if there is single
fail in the status column. I am generating Status if Result < Min then
it is fail or else it is pass. Now how to change the SubTotal row
below depending upon the condition. And is there any way to show the
Subtotal row directly from database. Any suggestion.
The easiest way to do this would be to use custom code (right-click non-display area of report, choose Properties and click the Code tab) - calculate the pass/fail score in the detail, display it in the group footer and reset it in the group header:
Dim PassFail As String
// Reset Pass or Fail status in group header
Public Function ResetAndDisplayStatusTitle() AS String
PassFail = "PASS" // Initialise status to pass
ResetAndDisplayStatusTitle = "Status"
End Function
// Calculate pass/fail on each detail row and remember any fails
Public Function CalculatePassFail() As String
Dim ThisResult As String
// Calculate whether this result is pass or fail
If Fields!Result.Value < Fields!Min.Value Then
ThisResult = "FAIL"
Else
ThisResult ="PASS"
End If
// Remember any failure as overall failure
If ThisResult = "FAIL" Then PassFail = "FAIL"
CalculatePassFail = ThisResult
End Function
Then you tie in the custom code to your cells in your table as follows:
In the value for the status column in your group header you put:
=Code.ResetAndDisplayStatusTitle()
In the value for the status column in the detail row you put:
=Code.CalculatePassFail()
In the value for the status column in the group footer you put:
=Code.PassFail
With respect to getting the subtotal row from the database directly from the database, there are a couple of ways depending on what result you are after.
Join the detail row to a subtotalling row in your SQL (so that the subtotal fields appear on every row in the dataset) and use those fields.
Again, use custom code (but this is probably overly complicated for subtotalling)
However, these tricks are only for strange circumstances and in general the normal out-of-the-box subtotalling can be tweaked to give the result you are after. If there is something specific you want to know, it is probably best to explain the problem in a separate question so that issue can be dealt with individually.

Resources