Forum OpenACS Development: Notifications tutorial

Collapse
Posted by David Bell on
I recently tried to use the notifications package. I found it under documented and kind of confusing. So once I worked it out I wrote the following document. I am still new to OpenACS so id like to get peoples opinion on this. Please correct me if I am doing anything wrong/bad in this tutorial.

OPEN-ACS Tutorial using Notifications package

The notifications package is a very useful but under-documented package. It allows you to send notifications through any defined communications medium (e.g. Email, sms) upon some event occuring within the system.

This tutorial steps through the process of integrating the notifications package with your package.
For this tutorial we will use the example of a jobs package, in which jobs for users are added, updated and removed (by a manager??) and those users need to be NOTIFIED of the existence of a new job or the update or removal of an existing job.
This example assumes that we already have our job system set up (objects defined etc.) and only need to add the notification part.

Create notification types

The first step is to create the notification types. To do this a script similar to the one below needs to be loaded into Postgresql. I create this script in a package/sql/postgresql/package-notifications-init.sql file. I then load this file from my create sql file. The script below creates a job_added notification type. A similar script needs to be written for job_updated and job_deleted if required.

create function inline_0() returns integer as '
declare
        impl_id integer;
        v_foo  integer;
begin
        -- the notification type impl
        impl_id := acs_sc_impl__new (
                      ''NotificationType'',
                      ''job_added_notif_type'',
                      ''jobs''
        );

        v_foo := acs_sc_impl_alias__new (
                    ''NotificationType'',
                    ''job_added_notif_type'',
                    ''GetURL'',
                    ''job::notification::get_url'',
                    ''TCL''
        );

        v_foo := acs_sc_impl_alias__new (
                    ''NotificationType'',
                    ''job_added_notif_type'',
                    ''ProcessReply'',
                    ''job::notification::process_reply'',
                    ''TCL''
        );

        PERFORM acs_sc_binding__new (
                    ''NotificationType'',
                    ''job_added_notif_type''
        );

        v_foo:= notification_type__new (
            NULL,
                impl_id,
                ''job_added_notif'',
                ''New Job Notification'',
                ''Notifications of new jobs added to the jobs system'',
        now(),
                NULL,
                NULL,
        NULL
        );

        -- enable the various intervals and delivery methods
        insert into notification_types_intervals
        (type_id, interval_id)
        select v_foo, interval_id
        from notification_intervals where name in (''instant'',''hourly'',''daily'');

        insert into notification_types_del_methods
        (type_id, delivery_method_id)
        select v_foo, delivery_method_id
        from notification_delivery_methods where short_name in (''email'');

return (0);
end;
' language 'plpgsql';

select inline_0();
drop function inline_0();

The next step is to setup our notification creation. A new notification must be added to the notification table for each job added. We do this using the notification::new procedure

        notification::new \
            -type_id [notification::type::get_type_id \
            -short_name job_added_notif] \
            -object_id $user_id\
            -response_id null\
            -notif_subject 'new job added' \
            -notif_text $job_description

This code would be added into the tcl after we created our new job. The $user_id is the OpenACS object id of the user to which the job has been assigned and the $job_description is some description of the job.

The final step is to setup the notification subscription process. In this example we want to let a user find out when they have a job assigned to them. To do this we put a link on our users personal page that allows them to subscribe to notifications of jobs assigned to them being added. The notifications/requests-new page is very handy in this situation.
Such a link would look something like this:
    <a href=”/notifications/request-new?object_id=@user_id@&type_id=@type_id@&return_url=@return_page@”> Notify me of new jobs assigned to me</a>

in the tcl for this page user_id should be set to the users id. type_id should be set to the id of the notification type, this can be got through the

[notification::type::get_type_id -short_name job_added_notif]

procedure as we used to add the notification above. The return_page should be set to the address we want the user to be redirected to after they have finished the subscription process.
This should be all you need to implement a notification system. For more examples look at the forums package.

Collapse
2: Re: Notifications tutorial (response to 1)
Posted by Robert Locke on
Thanks Dave for taking the time to write this.  Perhaps this can be incorporated into the notifications package docs.

Your timing is also interesting.  In trying to track down certain performance problems with openacs.org, I found a pretty serious one relating to the notifications package.

There is a scheduled proc called "notification::sweep::sweep_notifications", which is run as often as every minute if you have any instantaneous notifications defined (ie, forums).

In particular, there is a very nasty query in "sweep_notifications" which causes the openacs.org server load to rise dramatically, free memory to drop from 450MB to under 10MB, and causes the postmaster process to become a huge CPU hog (60 to 90%)!!!  Here it is:

            select notification_id,
                  notif_subject,
                  notif_text,
                  notif_html,
                  user_id,
                  type_id,
                  delivery_method_id,
                  response_id
            from notifications inner join notification_requests using
(type_id, object_id)
              inner join acs_objects on (notification_requests.request_id =
acs_objects.object_id)
              left outer join notification_user_map using (notification_id,
user_id)
            where sent_date is null
              and creation_date <= notif_date
              and interval_id = '2889'
            order by user_id, type_id;

Don has made some suggestions and I will look into it further.  But I thought folks should be informed in case anyone else is experiencing performance problems with their servers.

Collapse
3: Re: Notifications tutorial (response to 2)
Posted by Denis Roy on
Has anybody seen this performance problem on Oracle? If not is it likely to happen?

We are soon going live with AIESEC.net and I am very sure that we'd run into huge problems if it was the same for Oracle since notifications are very popular among our users.

Collapse
4: Re: Notifications tutorial (response to 1)
Posted by Joel Aufrecht on
nice job.  May I incorporate it into the Advanced Tutorial?
Collapse
5: Re: Notifications tutorial (response to 4)
Posted by David Bell on
Of course, It may need some checking first though as I am still a novice
Collapse
6: Re: Notifications tutorial (response to 1)
Posted by Lars Pind on
David,

It's great too see when frustration is turned into action. Thanks so much for contributing this.

One simplification: You no longer have to create the service contract implementation and notification type in SQL. You can do it in Tcl, with an 'after-install' callback, which is less verbose and also means that you can use only one version for both PostgreSQL and Oracle.

Take a look at packages/workflow/tcl/install-procs.tcl on the oacs-4-6 branch, in particular the proc 'workflow::install::register_notification_type'. You can setup the callback either in your .info file as in packages/workflow/workflow.info, or through the APM user interface.

I hope you find this simpler.

/Lars

Collapse
7: Re: Notifications tutorial (response to 1)
Posted by Jarkko Laine on
David (and Joel :),

Even if I'm being a late bird I can't resist reminding that there's no need to use foo variables in plpgsql. Just use PERFORM when you don't need the return value. There's a lot of places where they still exist in OACS code, but they are probably rests of automated porting from Oracle (or written by someone who has used them as example) and the habit shouldn't be copied to future development.

Collapse
Posted by Dirk Gomez on
There seems to be a cvs commit mailing list already. Where and how can one subscribe? We could have code reviews if the diffs were sent to this mailing.

The current CVS Commits forum is fairly useless without the diffs.

Collapse
Posted by Roberto Mello on
Hi Dirk,

I don't know why you posted to this thread, but...

The "mailing list" for CVS commits is actually just an alias, so no subscriptions or other mailing list services.

I wrote the CVS-to-Forums script a while ago with the intention of letting people follow the CVS commits. But it turns out that because the script is written in plain Tcl (not in AOLserver) and notifications does not have an PL/(pg)SQL API, then the script could never send out e-mails. Only insert into forums.

However, inserting to forums at commit time proved to be too time consuming, so the script was de-activated, apparently while I Was down in Brazil.

On July 28th I sent this mail to the gatekeepers alias:

------
Hi all,

I am re-writing my Tcl CVS logger script to be a bit different. Instead of logging the CVS commit log to CVS at commit time, it would be run off a scheduled job (perhaps as an OpenACS package) and do the following:

1) Scan for new commits
2) Insert the logs into the forums
3) Call notifications

I would like to add a 4th feature: automatic closing of bugs in bugtracker. It would work like this:

a) Parse CVS log and find out who made the commit.
b) Check if s/he has permission to close the bug in bug-tracker.
c) If so, close the bug. If not, perhaps send a URL to the admin with the bugs that were closed, asking him to close them by clicking on the URL.

I would use a syntax like Debian's bug tracking system (i.e. closes: #bug_number)

Objections? Suggestions?

Would there be any objections to making dotLRN CVS commits available in the forums? And for now, making them go to gatekeepers just like OpenACS commits? I'm working on intranetizing dotLRN and having the commits would be useful to track changes.
----

Adding the diffs could be another feature of the above. Hopefully this all would be a general openacs package that could be used by anyone.

Meanwhile, you can follow the CVS commits through Jeff's CVS tool, which syndicates the logs through RSS (not sure if it does diffs). http://xargs.net/tools/cvs/

-Roberto

Collapse
Posted by Dirk Gomez on
If you start work on a new project, you often take old code and adapt it to your needs. That is a very sensible approach, because you work off a "proven" code base.

Jarkko's post that you shouldn't use this or that PostgreSQL tricks although they are allover the OpenACS code just "inspired" me to post. If we have commits with diffs, we would have something like code reviews and I am convinced that we would have much less hackish or outdated code in our CVS repository.

Collapse
Posted by Jade Rubick on
This is a great contribution. Thanks, David.

You also need to create the procedures referred to as:

job::notification::get_url
job::notification::process_reply

I'm not totally sure what goes in them. In the workflow package, there are just stubs there. Ben Bytheway said that get_url takes an object_id and returns a URL.

This is my first stab at it in project-manager. No guarantees:

ad_library {

    Project Manager Projects Library

    Procedures that deal with tasks

    @creation-date 2003-12-18
    @author Jade Rubick <mailto:jader@bread.com>
    @cvs-id $Id$

}

namespace eval project_manager::task {}

ad_proc -public project_manager::task::get_url {
    object_id
} {

}

ad_proc -public project_manager::task::process_reply {
    reply_id
} {

}

Collapse
Posted by Jade Rubick on
The survey package seems to have an implementation of the get_url function.

For some reason, notifications are silently failing for me. I've tried many permutations:

set subject "subject"
set notification_text "Text"
set user_id [ad_conn user_id]

notification::new -type_id [notification::type::get_type_id -short_name task_added_notif] -object_id $user_id -response_id $user_id -notif_subject $subject -notif_text $notification_text

set object_id [return_object_id_of_a_task_in_project_manager]

notification::new -type_id [notification::type::get_type_id -short_name task_added_notif] -object_id $object_id -response_id $object_id -notif_subject $subject -notif_text $notification_text

notification::new -type_id [notification::type::get_type_id -short_name task_added_notif] -object_id $object_id -response_id null -notif_subject $subject -notif_text $notification_text

All fail silently. What am I doing wrong?

Collapse
Posted by Jade Rubick on