Skip to content

Commit

Permalink
WIP: Make creating and updating time period entries efficient
Browse files Browse the repository at this point in the history
  • Loading branch information
raviks789 committed Sep 12, 2023
1 parent 19fa458 commit 44ee546
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 78 deletions.
206 changes: 129 additions & 77 deletions application/forms/EntryForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use Icinga\Module\Notifications\Model\TimeperiodEntry;
use Icinga\Web\Session;
use ipl\Html\HtmlDocument;
use ipl\Orm\Behavior\Binary;
use ipl\Orm\Query;
use ipl\Scheduler\Contract\Frequency;
use ipl\Scheduler\OneOff;
use ipl\Scheduler\RRule;
Expand Down Expand Up @@ -123,6 +125,10 @@ public function loadEntry(int $scheduleId, int $id): self

$recipients = [];
foreach ($members as $member) {
if (! isset($values['membership_hash'])) {
$values['membership_hash'] = (new Binary([]))->toDb($member->membership_hash, null, null);
}

if ($member->contact_id !== null) {
$recipients[] = 'contact:' . $member->contact_id;
} else {
Expand Down Expand Up @@ -220,6 +226,7 @@ protected function assemble()
};

$this->addElement('hidden', 'timeperiod_id');
$this->addElement('hidden', 'membership_hash');

$this->addElement('textarea', 'description', [
'label' => $this->translate('Description'),
Expand Down Expand Up @@ -311,115 +318,160 @@ protected function assemble()
public function addEntry(int $scheduleId): void
{
$data = $this->formValuesToDb();
$membershipHash = $this->getMembershipHashFromRecipients($scheduleId);
$recipients = array_map(function ($recipient) {
return explode(':', $recipient, 2);
}, explode(',', $this->getValue('recipient')));

$db = Database::get();
$db->beginTransaction();

$db->insert('timeperiod', ['owned_by_schedule_id' => $scheduleId]);
$timeperiodId = $db->lastInsertId();
$members = $this->getScheduleMembersQuery($scheduleId, $membershipHash);
if ($members->count() > 0) {
$timeperiodId = $members->first()->timeperiod_id;
} else {
$db->insert('timeperiod', ['owned_by_schedule_id' => $scheduleId]);
$timeperiodId = $db->lastInsertId();
}

$db->insert('timeperiod_entry', $data + ['timeperiod_id' => $timeperiodId]);

foreach ($recipients as list($type, $id)) {
if ($type === 'contact') {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contact_id' => $id
]);
} elseif ($type === 'group') {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contactgroup_id' => $id
]);
if ($members->count() === 0) {
$binaryMemberHash = (new Binary([]))->toDb($membershipHash, null, null);
foreach ($recipients as list($type, $id)) {
if ($type === 'contact') {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contact_id' => $id,
'membership_hash' => $binaryMemberHash
]);
} elseif ($type === 'group') {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contactgroup_id' => $id,
'membership_hash' => $binaryMemberHash
]);
}
}
}

$db->commitTransaction();
}

/**
* Get schedule member query for the given schedule id and membership hash
*
* @param int $scheduleID
* @param string $hash
*
* @return Query
*/
private function getScheduleMembersQuery(int $scheduleID, string $hash)
{
return ScheduleMember::on(Database::get())
->with('timeperiod')
->filter(
Filter::all(
Filter::equal('timeperiod.owned_by_schedule_id', $scheduleID),
Filter::equal('schedule_member.membership_hash', $hash)
)
);
}

/**
* Get membership hash for the recipients in timeperiod entry for the given schedule
*
* @param int $scheduleID
*
* @return string
*/
public function getMembershipHashFromRecipients(int $scheduleID): string
{
$recipients = explode(',', $this->getValue('recipient'));
sort($recipients);
return sha1(sprintf('schedule:%d,%s', $scheduleID, implode(',', $recipients)), true);
}

public function editEntry(int $scheduleId, int $id): void
{
$data = $this->formValuesToDb();
$timeperiodId = (int) $this->getValue('timeperiod_id');

$db = Database::get();
$db->beginTransaction();
$binaryBehavior = new Binary([]);

$db->update('timeperiod_entry', $data, ['id = ?' => $id]);
$prevTimeperiodId = $this->getValue('timeperiod_id');
$membershipHash = $this->getMembershipHashFromRecipients($scheduleId);

$members = ScheduleMember::on($db)
->filter(Filter::all(
Filter::equal('schedule_id', $scheduleId),
Filter::equal('timeperiod_id', $timeperiodId)
));
$changedMembers = $this->getScheduleMembersQuery($scheduleId, $membershipHash);

$recipients = explode(',', $this->getValue('recipient'));
$prevMembers = $this->getScheduleMembersQuery($scheduleId, $this->getValue('membership_hash'));

$users = [];
$groups = [];
foreach ($recipients as $recipient) {
list($type, $id) = explode(':', $recipient, 2);
$db->beginTransaction();

if ($type === 'contact') {
$users[$id] = $id;
} elseif ($type === 'group') {
$groups[$id] = $id;
}
}
if ($changedMembers->count() > 0) {
$db->update(
'timeperiod_entry',
$data + ['timeperiod_id' => (int) $changedMembers->first()->timeperiod_id],
['id = ?' => $id]
);
$timeperiodId = $changedMembers->first()->timeperiod_id;
} elseif ($prevMembers->count() === 0) {
$db->update(
'timeperiod_entry',
$data + ['timeperiod_id' => $prevTimeperiodId],
['id = ?' => $id]
);
$timeperiodId = $prevTimeperiodId;
} else {
$db->insert('timeperiod', ['owned_by_schedule_id' => $scheduleId]);
$timeperiodId = $db->lastInsertId();

$usersToRemove = [];
$groupsToRemove = [];
foreach ($members as $member) {
if ($member->contact_id !== null) {
if (! isset($users[$member->contact_id])) {
$usersToRemove[] = $member->contact_id;
} else {
unset($users[$member->contact_id]);
}
} else {
if (! isset($groups[$member->contactgroup_id])) {
$groupsToRemove[] = $member->contactgroup_id;
} else {
unset($groups[$member->contactgroup_id]);
}
}
$db->update(
'timeperiod_entry',
$data + ['timeperiod_id' => $timeperiodId],
['id = ?' => $id]
);
}

if (! empty($usersToRemove)) {
$db->delete('schedule_member', [
'schedule_id = ?' => $scheduleId,
'timeperiod_id = ?' => $timeperiodId,
'contact_id IN (?)' => $usersToRemove
]);
}
$entriesForOldTimePeriod = TimeperiodEntry::on($db)
->with('timeperiod_entry.timeperiod')
->filter(Filter::equal('timeperiod.owned_by_schedule_id', $scheduleId))
->filter(Filter::equal('timeperiod_id', $prevTimeperiodId));

if ($entriesForOldTimePeriod->count() === 0) {
$db->delete(
'schedule_member',
[
'membership_hash = ?' => $this->getValue('membership_hash')
]
);

if (! empty($groupsToRemove)) {
$db->delete('schedule_member', [
'schedule_id = ?' => $scheduleId,
'timeperiod_id = ?' => $timeperiodId,
'contactgroup_id IN (?)' => $groupsToRemove
]);
$db->delete('timeperiod', ['id = ?' => $prevTimeperiodId]);
}

foreach ($users as $user) {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contact_id' => $user
]);
}
if ($changedMembers->count() === 0) {
$recipients = explode(',', $this->getValue('recipient'));

foreach ($groups as $group) {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contactgroup_id' => $group
]);
$binaryMemberHash = $binaryBehavior->toDb($membershipHash, null, null);
foreach ($recipients as $recipient) {
list($type, $recipientId) = explode(':', $recipient, 2);
if ($type === 'contact') {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contact_id' => $recipientId,
'membership_hash' => $binaryMemberHash
]);
} elseif ($type === 'group') {
$db->insert('schedule_member', [
'schedule_id' => $scheduleId,
'timeperiod_id' => $timeperiodId,
'contactgroup_id' => $recipientId,
'membership_hash' => $binaryMemberHash
]);
}
}
}

$db->commitTransaction();
Expand Down
12 changes: 11 additions & 1 deletion library/Notifications/Model/ScheduleMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Icinga\Module\Notifications\Model;

use ipl\Orm\Behavior\Binary;
use ipl\Orm\Behaviors;
use ipl\Orm\Model;
use ipl\Orm\Relations;

Expand All @@ -26,10 +28,18 @@ public function getColumns()
{
return [
'contact_id',
'contactgroup_id'
'contactgroup_id',
'membership_hash'
];
}

public function createBehaviors(Behaviors $behaviors)
{
$behaviors->add(new Binary([
'membership_hash'
]));
}

public function createRelations(Relations $relations)
{
$relations->hasOne('timeperiod', Timeperiod::class)
Expand Down

0 comments on commit 44ee546

Please sign in to comment.