All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eriwen.rtm.GroovyRtmUtils.groovy Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2010-2011 Eric Wendelin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.eriwen.rtm

import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.text.ParseException
import java.text.SimpleDateFormat
import org.apache.commons.codec.binary.Hex
import groovy.util.slurpersupport.GPathResult

/**
 * Utilities that are common to API elements without being tied to a specific
 * RTM API method.
 *
 * @author Eric Wendelin
 */
public class GroovyRtmUtils {
    private static MessageDigest digest
    private static final String encoding = 'UTF-8'
    private SimpleDateFormat rawDueDateFormat = new SimpleDateFormat('yyyy-MM-ddHH:mm:ss')
    private SimpleDateFormat defaultDateFmt = new SimpleDateFormat('MMM dd')
    private SimpleDateFormat dayOfWeekFmt = new SimpleDateFormat('EEEE')
    private SimpleDateFormat timeOfDayFmt = new SimpleDateFormat('HH:mm:ss')
    private SimpleDateFormat friendlyTimeFmt = new SimpleDateFormat('H:mm')
    int dayInMillis = 3600000 * 24
    int weekInMillis = dayInMillis * 7

    /*
    public def smartComparator = [
        compare: { Task o1, Task o2 ->
            if (o1.due == null || o1.due == "") return 1;
            if (o2.due == null || o2.due == "") return -1;
            if (o1.compareTo(o2) == 0) {
                if (o1.priority == "N") return 1;
                if (o2.priority == "N") return -1;
                return Integer.parseInt(o1.priority).compareTo(Integer.parseInt(o2.priority));
            }
            return o1.due.compareTo(o2.due)
        }
    ] as Comparator

    public def dueComparator = [
        compare: { Task o1, Task o2 ->
            if (o1.due == null || o1.due == "") return 1;
            if (o2.due == null || o2.due == "") return -1;
            return o1.due.compareTo(o2.due);
        }
    ] as Comparator

    public def priorityComparator = [
        compare: { Task o1, Task o2 ->
            if (o1.priority == "N") return 1;
            if (o2.priority == "N") return -1;
            //Higher number ~= lower priority
            return Integer.parseInt(o1.priority).compareTo(Integer.parseInt(o2.priority));
        }
    ] as Comparator

    public def nameComparator = [
        compare: { Task o1, Task o2 ->
            return o1.name.compareTo(o2.name)
        }
    ] as Comparator*/

    GroovyRtmUtils() {
        setMD5Digest()
    }

    /**
     * Gets the MD5 MessageDigest for signing API calls
     */
    private setMD5Digest() throws NoSuchAlgorithmException {
        digest = MessageDigest.getInstance('md5');
    }

    /**
     * Given a List of parameters, returns the RTM API signature
     *
     * @param params List of URL query parameters
     * @param secret the shared secret provided with an RTM API key
     * @return String API Signature
     */
    public String getApiSignature(List params, String secret) {
        digest.reset()
        digest.update(secret.getBytes(encoding))
        Collections.sort(params)
        params.each {
            //Remove '=' from parameters and decode them
            def param = URLDecoder.decode((it =~ /\=/).replaceAll(''), encoding)
            digest.update(param.getBytes(encoding))
        }
        return new String(Hex.encodeHex(digest.digest()))
    }

    /**
     * Given a URL, gets the response text or null if a server error occurred
     *
     * @param urlstr String representation of the URL to use for the call to the RTM REST API
     * @return String XML result from the RTM call
     * @throws GroovyRtmException when the HTTP request failed
     */
    public String getResponseText(String urlstr) throws GroovyRtmException {
        def connection = urlstr.toURL().openConnection()
        if (connection.responseCode == 200) {
            def responseText = connection.content.text
            return responseText
        } else if (connection.responseCode == -1) {
            throw new GroovyRtmException("Invalid URL: '${urlstr}' -- Ususally caused by not setting an API Key")
        }
        throw new GroovyRtmException("Server returned response code ${connection.responseCode}: ${connection.responseMessage}")
    }

    /**
     * Given a URL, gets the response text or throws an error if any problem,
     * including an error code response from RTM occurs
     *
     * @param urlstr String representation of the URL to use for the call to the RTM REST API
     * @return GPathResult XML result from the RTM call
     * @throws GroovyRtmException when RTM sends an error code
     */
    public GPathResult getRtmResponse(String urlstr) throws GroovyRtmException {
        String responseText = getResponseText(urlstr).trim()
        GPathResult resp = new XmlSlurper().parseText(responseText)
        if ([email protected]("ok")) {
            throw new GroovyRtmException([email protected]())
        }
        return resp
    }

    /**
     * Checks if RTM returned error-flagged XML
     *
     * @param result RTM REST API response XML
     * @return True if REST response indicates an error
     */
    public boolean isError(String responseText) {
        GPathResult resp = new XmlSlurper().parseText(responseText)
        [email protected]("ok")
    }

    /**
     * Gets the error message from the RTM response
     *
     * @param result RTM REST API response XML
     * @return String message
     */
    public String getErrorMessage(String responseText) {
        GPathResult resp = new XmlSlurper().parseText(responseText)
        [email protected]()
    }

    /**
     * Trim the string if length is greater than specified length
     *
     * @param string The string to truncate
     * @param length The preferred length of the returned String
     */
    public String trimString(String string, int length) {
        if(!string) {
            return '';
        } else if(string.length() > length) {
            return string.substring(0, length).trim();
        }
        return string;
    }

    /**
     * Given a date in the form YYYY-MM-DDTHH:MM:SSZ, return a friendly date
     * like "Tuesday" or "Mar 9" or "11:00AM"
     *
     * @param dateStr the date to parse as a String
     * @return Friendly date like "Tuesday" or "Mar 9" or "11:00AM"
     */
    public String formatFriendlyDate(String dateStr, boolean hasDueTime, Integer timezoneOffset = 0) {
        long nowMillis = getCurrentDayMillisAtMidnight(timezoneOffset);
        Date taskDate;

        //Return 'Never' for nothing or error
        try {
            taskDate = rawDueDateFormat.parse(trimString(dateStr, 10) + dateStr.substring(11, 19));
        } catch (NullPointerException npe) {
            return 'Never';
        } catch (ParseException pe) {
            return 'Never';
        } catch (StringIndexOutOfBoundsException sioobe) {
            //Occurs with dates before 1990 or something
            return 'Never';
        }

        long taskMillis = taskDate.getTime();
        taskDate.setTime(taskMillis + (timezoneOffset * 1000));
        boolean isToday = taskMillis >= nowMillis && taskMillis < nowMillis + dayInMillis;
        String friendlyDate;
        if (taskMillis >= nowMillis && taskMillis < (nowMillis + weekInMillis)) {
            if (taskMillis == nowMillis || (isToday && !hasDueTime)) {
                friendlyDate = 'Today';
            } else if (isToday && hasDueTime) {
                friendlyDate = friendlyTimeFmt.format(taskDate);
            } else if (taskMillis >= nowMillis + dayInMillis && taskMillis < nowMillis + (2 * dayInMillis)) {
                friendlyDate = 'Tomorrow';
            } else {
                //Return day of week if within a week from now
                friendlyDate = dayOfWeekFmt.format(taskDate);
            }
        } else {
            //Otherwise return MMM dd
            friendlyDate = defaultDateFmt.format(taskDate);
        }
        return friendlyDate;
    }
//
//    public String formatFriendlyDate(String dateStr, boolean hasDueTime) {
//        return formatFriendlyDate(dateStr, hasDueTime, 0);
//    }

    /**
     * Given a String representing the RTM repeat, return a friendly repeat
     * String value
     *
     * @param the repeat string to format
     * @return formatted repeat string like "every 3 blahs"
     */
    public String formatFriendlyRepeat(String repeatStr) {
        //Format is FREQ=MONTHLY;INTERVAL=1 or INTERVAL=4;FREQ=DAILY
        if (!repeatStr) {
            return "";
        }
        def repeatTerms = repeatStr.split(";");
        def freq = "";
        def interval = 0;
        for (String repeatTerm : repeatTerms) {
            int equalsPosition = repeatTerm.indexOf("=");
            if (repeatTerm.startsWith("INTERVAL")) {
                interval = Integer.parseInt(repeatTerm.substring(equalsPosition + 1));
            } else {
                freq = repeatTerm.substring(5, repeatTerm.length() - 2).toLowerCase();
                //Everything but days can be converted this way. HOURLY, DAILY, WEEKLY, MONTHLY, YEARLY
                if (freq.equals("dai")) {
                    freq = "day";
                }
            }
        }
        String friendlyRepeat = "every ${interval} ${freq}"
        if (interval > 1) {
            friendlyRepeat += "s";
        }
        return friendlyRepeat;
    }

    /**
     * Given a date string, return if the date is overdue
     *
     * @param dateStr the date to check
     * @return True if the date is overdue (past today ignoring time)
     */
    public boolean isOverdue(String dateStr, int timezoneOffset) {
        //Check for null date
        if (!dateStr || dateStr.length() < 19) {
            return false;
        }
        Date taskDate;
        try {
            taskDate = rawDueDateFormat.parse(trimString(dateStr, 10) + dateStr.substring(11, 19));
            if ((getCurrentDayMillisAtMidnight(timezoneOffset) - taskDate.getTime()) > 0) {
                return true;
            }
            return false;
        } catch (ParseException pe) {
            pe.printStackTrace();
            return false;
        }
    }

    public long getCurrentDayMillisAtMidnight(long timezoneOffset) {
        Calendar now = Calendar.getInstance();
        now.set(Calendar.HOUR_OF_DAY, 0);
        now.set(Calendar.MINUTE, 0);
        now.set(Calendar.SECOND, 0);
        now.set(Calendar.MILLISECOND, 0);
        //Isn't java.util.Calendar great?
        long nowMillis = now.getTimeInMillis();
        nowMillis -= timezoneOffset * 1000;
        return nowMillis;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy