Script to add relative due date for next available in sequential project?

Basically the title. I know this has been discussed a lot before, but in my searching I’m having a difficult time finding someone who has successfully implemented this.

Here’s what I need in more detail:

Suppose I’m trying to get better about cleaning my home, so I’ve broken it up into 15 different tasks (dust the pictures and furniture, vacuum the floor, mop the floor, scrub the toilet, etc) that are part of a sequential project, which repeats after completion. I want to do one task from that project every day, and after marking each day’s task complete, I want the next task to become available with a due date set for tomorrow.

I don’t think Omnifocus supports this natively as a feature, so I’m hoping someone can help me with a script that will run daily on my Mac, checking for tasks in a certain project that have availability: next available, and either flag them or give them a due date +1 days from today.

Does this work for you ?

(() => {
    'use strict';
    // Twitter: @unlocked2412

    // USER DATA ------------------------------------------
    const projName = 'Plant a Tree';

    // OMNI JS CODE ---------------------------------------
    const omniJSContext = strName => {
        // main :: IO ()
        const main = () => {
            const
                mbNext = bindMay(
                    // Project exists ?
                    find(x => x.name === strName)(
                        flattenedProjects
                    )
                )(
                    // Does it have a next action ?
                    proj => null === proj.nextTask ? (
                        Nothing()
                    ) : Just(proj.nextTask)
                );

            return mbNext.Nothing ? (
                mbNext.Nothing
            ) : (() => {
                return (
                    mbNext.Just.flagged = true,
                    mbNext.Just
                )
            })();
        };

        // FUNCTIONS --
        // JS Prelude --------------------------------------------------
        // Just :: a -> Maybe a
        const Just = x => ({
            type: 'Maybe',
            Nothing: false,
            Just: x
        });

        // Nothing :: Maybe a
        const Nothing = () => ({
            type: 'Maybe',
            Nothing: true,
        });

        // Tuple (,) :: a -> b -> (a, b)
        const Tuple = a =>
            b => ({
                type: 'Tuple',
                '0': a,
                '1': b,
                length: 2
            });

        // bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
        const bindMay = mb =>
            // Nothing if mb is Nothing, or the application of the
            // (a -> Maybe b) function mf to the contents of mb.
            mf => mb.Nothing ? (
                mb
            ) : mf(mb.Just);

        // find :: (a -> Bool) -> [a] -> Maybe a
        const find = p =>
            // Just the first element in xs which 
            // matches the predicate p, or
            // Nothing if no match is found.
            xs => xs.constructor.constructor.name !== (
                'GeneratorFunction'
            ) ? (() => {
                const
                    ys = list(xs),
                    i = ys.findIndex(p);
                return -1 !== i ? (
                    Just(ys[i])
                ) : Nothing();
            })() : findGen(p)(xs);

        // findGen :: (a -> Bool) -> Gen [a] -> Maybe a
        const findGen = p =>
            // Just the first match for the predicate p
            // in the generator stream xs, or Nothing
            // if no match is found.
            xs => {
                const
                    mb = until(tpl => {
                        const nxt = tpl[0];
                        return nxt.done || p(nxt.value);
                    })(
                        tpl => Tuple(tpl[1].next())(
                            tpl[1]
                        )
                    )(Tuple(xs.next())(xs))[0];
                return mb.done ? (
                    Nothing()
                ) : Just(mb.value);
            };

        // findIndex :: (a -> Bool) -> [a] -> Maybe Int
        const findIndex = p =>
            //  Just the index of the first element in
            //  xs for which p(x) is true, or
            //  Nothing if there is no such element.
            xs => {
                const i = [...xs].findIndex(p);
                return -1 !== i ? (
                    Just(i)
                ) : Nothing();
            };

        // length :: [a] -> Int
        const length = xs =>
            // Returns Infinity over objects without finite
            // length. This enables zip and zipWith to choose
            // the shorter argument when one is non-finite,
            // like cycle, repeat etc
            'GeneratorFunction' !== xs.constructor.constructor.name ? (
                xs.length
            ) : Infinity;

        // list :: StringOrArrayLike b => b -> [a]
        const list = xs =>
            // xs itself, if it is an Array,
            // or an Array derived from xs.
            Array.isArray(xs) ? (
                xs
            ) : Array.from(xs || []);

        // until :: (a -> Bool) -> (a -> a) -> a -> a
        const until = p => f => x => {
            let v = x;
            while (!p(v)) v = f(v);
            return v;
        };

        return main();
    };


    // OmniJS Context Evaluation ------------------------------------------------
    return 0 < Application('OmniFocus').documents.length ? (
        Application('OmniFocus').evaluateJavascript(
            `(${omniJSContext})(${JSON.stringify(projName)})`
        )
    ) : 'No documents open in OmniFocus.'
})();

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.