Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Application/Controller/XMLRPC/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ public function setCommenceTicketState($ticket_id) {
throw new Exception(__FUNCTION__.': ticket is not in initial state!',1304);
}

$commenceState = ProjectTicketState::getCommenceState($ticket['project_id'], $ticket['ticket_type']);
$commenceState = ProjectTicketState::getCommenceState($ticket['project_id'], $ticket['ticket_type'], $ticket_id);
if(!$commenceState) {
throw new Exception(__FUNCTION__.': ticket has no commence state!',1305);
}
Expand Down
1 change: 1 addition & 0 deletions src/Application/Migrations/04_projects.sql
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ CREATE TABLE tbl_project_ticket_state
ticket_type enum_ticket_type NOT NULL,
ticket_state enum_ticket_state NOT NULL,
service_executable boolean NOT NULL DEFAULT false,
skip_on_dependent boolean NOT NULL DEFAULT false,
CONSTRAINT tbl_project_ticket_state_pk PRIMARY KEY (project_id, ticket_type, ticket_state),
CONSTRAINT tbl_project_ticket_state_project_fk FOREIGN KEY (project_id)
REFERENCES tbl_project (id) MATCH SIMPLE
Expand Down
4 changes: 2 additions & 2 deletions src/Application/Migrations/06_tickets.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ $BODY$
DECLARE
next_state record;
BEGIN
next_state := ticket_state_next(NEW.project_id, NEW.ticket_type, NEW.ticket_state);
next_state := ticket_state_next(NEW.project_id, NEW.ticket_type, NEW.ticket_state, NEW.id);

NEW.ticket_state_next := next_state.ticket_state;
NEW.service_executable := next_state.service_executable;
Expand Down Expand Up @@ -76,7 +76,7 @@ $BODY$
(progress, ticket_state_next, service_executable)
= (tp, (n).ticket_state, (n).service_executable)
FROM (
SELECT id, ticket_state_next(t2.project_id, t2.ticket_type, t2.ticket_state) AS n, ticket_progress(t2.id) as tp
SELECT id, ticket_state_next(t2.project_id, t2.ticket_type, t2.ticket_state, t2.id) AS n, ticket_progress(t2.id) as tp
FROM tbl_ticket t2
WHERE t2.project_id = param_project_id AND param_project_id IS NOT NULL
) AS x
Expand Down
81 changes: 67 additions & 14 deletions src/Application/Migrations/15_function_ticket_state.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ BEGIN;

SET ROLE TO postgres;

CREATE OR REPLACE FUNCTION ticket_state_next(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state)
CREATE OR REPLACE FUNCTION ticket_state_next(param_ticket_id bigint)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jjeising @pegro
Continuing discussion
This doesn't work, Ticket::expandRecording calls Ticket::queryPreviousState with state='preparing', differing from the current state of the ticket, which would usually be cutting.
I'd suggest using an additional parameter skip_dependent DEFAULT FALSE and adding a new function (maybe with a trigger as suggested) ticket_is_master to pass that in from outside and keep this function completely ticket agnostic.

Copy link
Member

@pegro pegro May 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said before, if determining a next/previous ticket state depends on more as just the ticket type and state, I'm fine with changing all functions to just requiring a ticket_id and migrate all users accordingly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with changing all functions to just requiring a ticket_id

The new issue here is: ticket_state_next is also used with a state different from the current state of the ticket (with param_ticket_id).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to query for all possible ticket states of the current ticket, I would then add a second optional parameter param_state as a state of reference which defaults to the current ticket state.

So you could call ticket_state_next(param_ticket_id) to get the next state in relation to the current state. And you could call ticket_state_next(param_ticket_id, param_ticket_state) to query for the next state the ticket would get advanced to, if the ticket would be in state param_ticket_state.

At least that's what I'd suggest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zuntrax That's what we discussed previously, can you implement that?

RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
Expand All @@ -11,17 +11,61 @@ BEGIN
SELECT
pts.ticket_state, pts.service_executable
FROM
tbl_ticket_state ts1
tbl_ticket t
JOIN
tbl_project_ticket_state pts ON pts.ticket_type = ts1.ticket_type AND pts.ticket_state = ts1.ticket_state
tbl_ticket_state ts_this ON ts_this.ticket_type = t.ticket_type AND ts_this.ticket_state = t.ticket_state
JOIN
tbl_ticket_state ts2 ON ts1.ticket_type = ts2.ticket_type AND ts1.sort > ts2.sort
tbl_project_ticket_state pts ON pts.project_id = t.project_id AND pts.ticket_type = t.ticket_type
JOIN
tbl_ticket_state ts_other ON ts_other.ticket_type = pts.ticket_type AND ts_other.ticket_state = pts.ticket_state
WHERE
pts.project_id = param_project_id AND
ts2.ticket_type = param_ticket_type AND
ts2.ticket_state = param_ticket_state
ORDER BY
ts1.sort ASC
t.id = param_ticket_id AND
ts_other.sort > ts_this.sort AND
(pts.skip_on_dependent = FALSE OR
( /* is master encoding ticket */
SELECT ep.depends_on
FROM tbl_ticket t
JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
WHERE t.id = param_ticket_id
) IS NULL )
ORDER BY ts_other.sort ASC
LIMIT 1;
IF NOT FOUND THEN
RETURN QUERY SELECT NULL::enum_ticket_state, false;
END IF;
END
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION ticket_state_previous(param_ticket_id bigint)
RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
BEGIN
RETURN QUERY
SELECT
pts.ticket_state, pts.service_executable
FROM
tbl_ticket t
JOIN
tbl_ticket_state ts_this ON ts_this.ticket_type = t.ticket_type AND ts_this.ticket_state = t.ticket_state
JOIN
tbl_project_ticket_state pts ON pts.project_id = t.project_id AND pts.ticket_type = t.ticket_type
JOIN
tbl_ticket_state ts_other ON ts_other.ticket_type = pts.ticket_type AND ts_other.ticket_state = pts.ticket_state
WHERE
t.id = param_ticket_id AND
ts_other.sort < ts_this.sort AND
(pts.skip_on_dependent = FALSE OR
( /* is master encoding ticket */
SELECT ep.depends_on
FROM tbl_ticket t
JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
WHERE t.id = param_ticket_id
) IS NULL )
ORDER BY ts_other.sort DESC
LIMIT 1;
IF NOT FOUND THEN
RETURN QUERY SELECT NULL::enum_ticket_state, false;
Expand All @@ -30,7 +74,7 @@ END
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION ticket_state_previous(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state)
CREATE OR REPLACE FUNCTION ticket_state_previous(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state, param_ticket_id bigint default NULL)
RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
Expand All @@ -47,7 +91,16 @@ BEGIN
WHERE
pts.project_id = param_project_id AND
ts2.ticket_type = param_ticket_type AND
ts2.ticket_state = param_ticket_state
ts2.ticket_state = param_ticket_state AND
(pts.skip_on_dependent = false OR
( /* is master encoding ticket */
SELECT ep.depends_on
FROM tbl_ticket t
JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
WHERE t.id = param_ticket_id
) IS NULL
)
ORDER BY
ts1.sort DESC
LIMIT 1;
Expand Down Expand Up @@ -96,7 +149,7 @@ END
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION ticket_state_commence(param_project_id bigint, param_ticket_type enum_ticket_type)
CREATE OR REPLACE FUNCTION ticket_state_commence(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_id bigint)
RETURNS enum_ticket_state AS
$$
DECLARE
Expand All @@ -111,7 +164,7 @@ BEGIN
ret := (SELECT ticket_state_initial(param_project_id, param_ticket_type));

WHILE ret IS NOT NULL LOOP
SELECT * INTO next_state FROM ticket_state_next(param_project_id, param_ticket_type, ret);
SELECT * INTO next_state FROM ticket_state_next(param_ticket_id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: eliminating ret from parameters will probably produce endless loops.

IF NOT FOUND THEN
ret := NULL;
EXIT;
Expand All @@ -129,4 +182,4 @@ END
$$
LANGUAGE plpgsql;

COMMIT;
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
BEGIN;

SET ROLE TO postgres;

DROP FUNCTION ticket_state_next(bigint, enum_ticket_type, enum_ticket_state);
CREATE OR REPLACE FUNCTION ticket_state_next(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state, param_ticket_id bigint default NULL)
RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
BEGIN
RETURN QUERY
SELECT
pts.ticket_state, pts.service_executable
FROM
tbl_ticket_state ts1
JOIN
tbl_project_ticket_state pts ON pts.ticket_type = ts1.ticket_type AND pts.ticket_state = ts1.ticket_state
JOIN
tbl_ticket_state ts2 ON ts1.ticket_type = ts2.ticket_type AND ts1.sort > ts2.sort
WHERE
pts.project_id = param_project_id AND
ts2.ticket_type = param_ticket_type AND
ts2.ticket_state = param_ticket_state AND
(pts.skip_on_dependent = false OR
( /* is master encoding ticket */
SELECT ep.depends_on
FROM tbl_ticket t
JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
WHERE t.id = param_ticket_id
) IS NULL
)
ORDER BY
ts1.sort ASC
LIMIT 1;
IF NOT FOUND THEN
RETURN QUERY SELECT NULL::enum_ticket_state, false;
END IF;
END
$$
LANGUAGE plpgsql;

DROP FUNCTION ticket_state_previous(bigint, enum_ticket_type, enum_ticket_state);
CREATE OR REPLACE FUNCTION ticket_state_previous(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state, param_ticket_id bigint default NULL)
RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
BEGIN
RETURN QUERY
SELECT
pts.ticket_state, pts.service_executable
FROM
tbl_ticket_state ts1
JOIN
tbl_project_ticket_state pts ON pts.ticket_type = ts1.ticket_type AND pts.ticket_state = ts1.ticket_state
JOIN
tbl_ticket_state ts2 ON ts1.ticket_type = ts2.ticket_type AND ts1.sort < ts2.sort
WHERE
pts.project_id = param_project_id AND
ts2.ticket_type = param_ticket_type AND
ts2.ticket_state = param_ticket_state AND
(pts.skip_on_dependent = false OR
( /* is master encoding ticket */
SELECT ep.depends_on
FROM tbl_ticket t
JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
WHERE t.id = param_ticket_id
) IS NULL
)
ORDER BY
ts1.sort DESC
LIMIT 1;
IF NOT FOUND THEN
RETURN QUERY SELECT NULL::enum_ticket_state, false;
END IF;
END
$$
LANGUAGE plpgsql;

DROP FUNCTION ticket_state_commence(bigint, enum_ticket_type);
CREATE OR REPLACE FUNCTION ticket_state_commence(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_id bigint)
RETURNS enum_ticket_state AS
$$
DECLARE
ret enum_ticket_state;
next_state record;
BEGIN
-- special case: meta ticket, since it has no serviceable states
IF param_ticket_type = 'meta' THEN
RETURN 'staged'::enum_ticket_state;
END IF;

ret := (SELECT ticket_state_initial(param_project_id, param_ticket_type));

WHILE ret IS NOT NULL LOOP
SELECT * INTO next_state FROM ticket_state_next(param_project_id, param_ticket_type, ret, param_ticket_id);
IF NOT FOUND THEN
ret := NULL;
EXIT;
END IF;

-- exit, if serviceable state is found
EXIT WHEN next_state.service_executable IS TRUE;

-- otherwise set current state as possible commence state
ret := next_state.ticket_state;
END LOOP;

RETURN ret;
END
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION update_ticket_next_state()
RETURNS trigger AS
$BODY$
DECLARE
next_state record;
BEGIN
next_state := ticket_state_next(NEW.project_id, NEW.ticket_type, NEW.ticket_state, NEW.id);

NEW.ticket_state_next := next_state.ticket_state;
NEW.service_executable := next_state.service_executable;

RETURN NEW;
END
$BODY$
LANGUAGE plpgsql VOLATILE;

CREATE OR REPLACE FUNCTION update_all_tickets_progress_and_next_state(param_project_id bigint)
RETURNS VOID AS
$BODY$
BEGIN

UPDATE tbl_ticket t SET
(progress, ticket_state_next, service_executable)
= (tp, (n).ticket_state, (n).service_executable)
FROM (
SELECT id, ticket_state_next(t2.project_id, t2.ticket_type, t2.ticket_state, t2.id) AS n, ticket_progress(t2.id) as tp
FROM tbl_ticket t2
WHERE t2.project_id = param_project_id AND param_project_id IS NOT NULL
) AS x
WHERE t.id = x.id;

END;
$BODY$
LANGUAGE plpgsql VOLATILE;

ALTER TABLE tbl_project_ticket_state ADD COLUMN skip_on_dependent BOOLEAN NOT NULL DEFAULT FALSE;

COMMIT;
18 changes: 9 additions & 9 deletions src/Application/Model/ProjectTicketState.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,30 @@ class ProjectTicketState extends Model {
];

// TODO: use Ticket::queryNextState / queryPreviousState?
public static function getNextState($project, $type, $state) {
public static function getNextState($project, $type, $state, $ticket) {
$handle = Database::$Instance->query(
'SELECT * FROM ticket_state_next(?, ?, ?)',
[$project, $type, $state]
'SELECT * FROM ticket_state_next(?, ?, ?, ?)',
[$project, $type, $state, $ticket]
);
$row = $handle->fetch();

return ($row === false)? null : $row;
}

public static function getPreviousState($project, $type, $state) {
public static function getPreviousState($project, $type, $state, $ticket) {
$handle = Database::$Instance->query(
'SELECT * FROM ticket_state_previous(?, ?, ?)',
[$project, $type, $state]
'SELECT * FROM ticket_state_previous(?, ?, ?, ?)',
[$project, $type, $state, $ticket]
);

$row = $handle->fetch();
return ($row === false)? null : $row;
}

public static function getCommenceState($project, $type) {
public static function getCommenceState($project, $type, $ticket) {
$handle = Database::$Instance->query(
'SELECT ticket_state_commence(?, ?)',
[$project, $type]
'SELECT ticket_state_commence(?, ?, ?)',
[$project, $type, $ticket]
);

return $handle->fetch()['ticket_state_commence'];
Expand Down
12 changes: 7 additions & 5 deletions src/Application/Model/Ticket.php
Original file line number Diff line number Diff line change
Expand Up @@ -855,12 +855,13 @@ public function queryPreviousState($state = null) {
return (new Database_Query(''))
->select('ticket_state')
->from(
'ticket_state_previous(?, ?, ?)',
'ticket_state_previous(?, ?, ?, ?)',
'previous_state',
[
$this['project_id'],
$this['ticket_type'],
($state === null)? $this['ticket_state'] : $state
($state === null)? $this['ticket_state'] : $state,
$this['id']
]
);
}
Expand All @@ -869,16 +870,17 @@ public function queryNextState($state = null) {
return (new Database_Query(''))
->select('ticket_state')
->from(
'ticket_state_next(?, ?, ?)',
'ticket_state_next(?, ?, ?, ?)',
'next_state',
[
$this['project_id'],
$this['ticket_type'],
($state === null)? $this['ticket_state'] : $state
($state === null)? $this['ticket_state'] : $state,
$this['id']
]
);
}

public function findNextForAction($state) {
$next = null;

Expand Down
Loading