Last active
December 9, 2023 22:09
-
-
Save logic2design/e6b61ab067483c92ebd088b31656e31a to your computer and use it in GitHub Desktop.
Will create a daily Journal entry with Daily Events, Quote of the Day, World and Local News Headlines and Weather Condtion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################################################# | |
# Title: Create Daily Journal Entry in Devonthink, with Daily Events, Quote of the Day, World & Local News and Weather Condtion | |
################################################################################# | |
# Iain Dunn | |
# Logic2design.com | |
# logic2design@icloud.com | |
# Contributors, inspration and sources | |
# Based on script by Chuck Lane October 2, 2013 | |
# Updated and optimized for DEVONthink 3 by Christian Grunenberg April 30, 2019# | |
# Weather Forcast - korm - https://discourse.devontechnologies.com/t/get-weather-for-daily-journal/59577 | |
# Weather Icons - Cesare Forelli - https://cdf1982.com/2018/12/12/applescript_with_darksky_weather_plus_notifications_and_imessage.html | |
# Calendar Events - Nigel Garvey - https://macscripter.net/viewtopic.php?id=41273&p=2 | |
################################################################################# | |
# Configuration | |
################################################################################# | |
### Install these helpers | |
-- CalendarLib EC, https://latenightsw.com/support/freeware/, install into /Users/(User)/Library/Script Libraries | |
-- JSON Helper, https://apps.apple.com/us/app/json-helper-for-applescript/id453114608?mt=12 | |
-- Location Helper, https://apps.apple.com/us/app/location-helper/id488536386?mt=12 | |
### Variables | |
property holidayCalendar : "Australian Holidays" | |
property birthdayCalendar : "Birthdays" | |
--News Headlines | |
property worldNews : "http://feeds.bbci.co.uk/news/world/rss.xml" | |
property localNews : "https://thewest.com.au/news/wa/rss" | |
set numHeadlines to 4 | |
global theDate, targetPath, numHeadlines | |
--Quote of the Day | |
property quoteoftheDay : "feed://feeds.feedburner.com/quotationspage/qotd" | |
--Weather Condition | |
property weatherAPI : "???" -- obtain from https://openweathermap.org | |
--Date | |
set theDate to current date | |
--Database | |
property journalDatabase : "~/Databases/Jornal.dtBase2" | |
### Formatiing | |
-- Time | |
set theTime to time string of theDate | |
if character 5 of theTime is ":" then | |
set theTime to (characters 1 through 4 of theTime) & (characters 8 through 10 of theTime) as string | |
else | |
set theTime to (characters 1 through 5 of theTime) & (characters 9 through 11 of theTime) as string | |
end if | |
--Month | |
set theMonth to month of theDate as string | |
set numMonth to (month of theDate as integer) as string | |
if the (length of numMonth) < 2 then set numMonth to "0" & numMonth | |
--Day | |
set theDay to day of theDate as string | |
set shortDay to theDay -- shortDay won't have a leading zero | |
if the (length of theDay) < 2 then set theDay to "0" & theDay | |
set suffixList to {"st", "nd", "rd"} | |
set theIndex to last character of theDay as integer | |
if (theIndex > 0) and (theIndex < 4) and the first character of theDay is not "1" then | |
set daySuffix to item theIndex of suffixList | |
else | |
set daySuffix to "th" | |
end if | |
--Weekday | |
set longWeekday to weekday of theDate as string | |
set shortWeekday to characters 1 thru 3 of longWeekday | |
--Year | |
set theYear to year of theDate as string | |
set theYearMonth to theYear & "-" & numMonth | |
--set theMonth to numMonth & "-" & theMonth | |
--Content | |
set recordName to numMonth & "_" & theDay & "_" & theYear & "_" & shortWeekday | |
set newHeading to getWeather() & " " & theTime & return | |
set mainHeading to longWeekday & "," & space & shortDay & daySuffix & space & theMonth & return | |
set newParagraph to " " | |
set calEvents to main() & return | |
--Text | |
property blueColor : {0, 0, 30000} | |
property headerColor : {40000, 20000, 0} | |
property blackColor : {0, 0, 0} | |
property dateColor : {30000, 30000, 30000} | |
################################################################################# | |
# Script | |
################################################################################# | |
tell application "DEVONthink 3" | |
try | |
activate | |
set theDatabase to open database journalDatabase | |
set myGroup to create location "/Journal/" & "/" & theYear & "/" & numMonth & "-" & theMonth | |
set recordName to theYear & "-" & numMonth & "-" & theDay & " " & shortWeekday | |
if not (exists child recordName of myGroup) then | |
set myRecord to create record with {name:recordName, rich text:"", type:rtfd, tags:theYear & "," & theMonth} in myGroup | |
tell text of myRecord | |
make paragraph at beginning with properties {alignment:center, font:"Zapfino", size:18, color:dateColor} with data mainHeading | |
bold first paragraph | |
unbold last character | |
set properties of last character to {font:"Avenir", size:14} | |
--Show any holidays and/or birthdays | |
make new paragraph at end with properties {alignment:center, font:"Avenir", size:14, color:blueColor} with data calEvents & return | |
set color of last character to blackColor | |
set properties of last character to {font:"Avenir", size:14} | |
--Show the daily quote | |
set myQuote to my getQuote() | |
set the last character to the last character & myQuote & return | |
set alignment of last character to left | |
--Show specified number of World news headlines | |
set myNews to my getNewsWorld() | |
set the last character to the last character & "WORLD HEADLINES:" & return | |
bold last paragraph | |
unbold last character | |
repeat with i from 1 to (count of items of myNews) by 2 | |
set the last character to the last character & item i of myNews & return | |
set the URL of the last paragraph to item (i + 1) of myNews | |
end repeat | |
make new paragraph at end with data newParagraph | |
set properties of last paragraph to {font:"Avenir", size:14, color:blackColor} | |
--Show specified number of Local news headlines | |
set myNews to my getNewsLocal() | |
set the last character to the last paragraph & return & "LOCAL HEADLINES:" & return | |
bold last paragraph | |
unbold last character | |
repeat with i from 1 to (count of items of myNews) by 2 | |
set the last character to the last character & item i of myNews & return | |
set the URL of the last paragraph to item (i + 1) of myNews | |
end repeat | |
end tell | |
else -- Record already exists, just add new weather/time header | |
set myRecord to child recordName in myGroup | |
end if | |
tell text of myRecord | |
set the last character to the last character & return & return | |
make new paragraph at end with data newHeading | |
#make new paragraph at end with data ((getWeather() & theTime & return) as string) | |
set properties of last paragraph to {font:"Avenir", size:16, alignment:left, superscript:2, color:headerColor} | |
italicize last paragraph | |
set properties of last character to {font:"Avenir", size:16, alignment:left, superscript:0, color:blackColor} | |
set last character to last character & "■ " | |
end tell | |
set newHeading to "" | |
on error errMsg number errNum | |
hide progress indicator | |
display alert (localized string "An error occured when adding the note. ") & errMsg | |
end try | |
set the clipboard to "" | |
end tell | |
return | |
################################################################################# | |
# Functions | |
################################################################################# | |
--Get Weather Conditions | |
on getWeather() | |
tell application "JSON Helper" | |
try | |
(* Get my latitude and longitude*) | |
tell application "Location Helper" | |
set listCoords to get location coordinates | |
set _lat to item 1 of listCoords as text | |
set _lon to item 2 of listCoords as text | |
end tell | |
set Weather_Display to "" | |
(* Get current weather from OpenWeather *) | |
set getWeathersource to fetch JSON from ("https://api.openweathermap.org/data/2.5/weather?lat=" & _lat & "&lon=" & _lon & "&exclude=minutely,hourly&units=metric&appid=" & weatherAPI) | |
set getWeather to "High: " & temp_max of main of getWeathersource & return & return & "Low: " & temp_min of main of getWeathersource & return & return & "Current: " & temp of main of getWeathersource & return & return & "Humidity: " & humidity of main of getWeathersource & return & return & "Conditions: " & description of item 1 of weather of getWeathersource | |
set Current_Temp to (round (temp of main of getWeathersource)) & "°" | |
set Daily_Temp to "Min " & (round (temp_min of main of getWeathersource)) & "° " & "to a " & "Max " & (round (temp_max of main of getWeathersource)) & "°" | |
set Weather_Display to "## Weather" & return & return & getWeather & return & return | |
end try | |
end tell | |
if Weather_Display contains "clear" then | |
set WeatherIcon to "☀️" | |
else if Weather_Display contains "thunderstorm" then | |
set WeatherIcon to "🌧⚡️" | |
else if Weather_Display contains "drizzle" then | |
set WeatherIcon to "🌦" | |
else if Weather_Display contains "rain" then | |
set WeatherIcon to "🌧" | |
else if Weather_Display contains "snow" then | |
set WeatherIcon to "❄️" | |
else if Weather_Display contains "sleet" then | |
set WeatherIcon to "❄️" | |
else if Weather_Display contains "mist" then | |
set WeatherIcon to "🌫" | |
else if Weather_Display contains "fog" then | |
set WeatherIcon to "🌁" | |
else if Weather_Display contains "squalls" then | |
set WeatherIcon to "💨" | |
else if Weather_Display contains "smoke" then | |
set WeatherIcon to "🔥" | |
else if Weather_Display contains "haze" then | |
set WeatherIcon to "🌫" | |
else if Weather_Display contains "clouds" then | |
set WeatherIcon to "⛅️" | |
else | |
set WeatherIcon to "🌡" | |
end if | |
set theForecast to "" | |
set theForecast to WeatherIcon & " " & Current_Temp | |
return theForecast | |
end getWeather | |
--Get Quote of the Day | |
on getQuote() | |
tell application "DEVONthink 3" | |
try | |
set myQuote to "" | |
set getSource to download markup from quoteoftheDay | |
set getFeed to get items of feed getSource | |
if items of getFeed is not {} then | |
set randItem to some item of getFeed | |
set myQuote to description of randItem & return & "= " & title of randItem & " =" & return | |
end if | |
end try | |
return myQuote | |
end tell | |
end getQuote | |
--Get the news headlines World | |
on getNewsWorld() | |
set myNews to {} | |
tell application "DEVONthink 3" | |
try | |
set getNewsSource to download markup from worldNews | |
#set getNewsSource to download markup from "https://thewest.com.au/news/wa/rss" | |
#set getNewsSource to download markup from "feed://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml" | |
set getNewsFeed to items 1 thru numHeadlines of (get items of feed getNewsSource) | |
repeat with theItems in getNewsFeed | |
set end of myNews to title of theItems | |
set end of myNews to link of theItems | |
end repeat | |
end try | |
return myNews | |
end tell | |
end getNewsWorld | |
--Get the news headlines Local | |
on getNewsLocal() | |
set myNews to {} | |
tell application "DEVONthink 3" | |
try | |
set getNewsSource to download markup from localNews | |
set getNewsFeed to items 1 thru numHeadlines of (get items of feed getNewsSource) | |
repeat with theItems in getNewsFeed | |
set end of myNews to title of theItems | |
set end of myNews to link of theItems | |
end repeat | |
end try | |
return myNews | |
end tell | |
end getNewsLocal | |
###Days Events | |
use script "CalendarLib EC" | |
use scripting additions | |
property calendarList : {birthdayCalendar, holidayCalendar} -- An empty list means all calendars. | |
main() | |
on main() | |
set {dateRangeStart, dateRangeEnd} to getDateRange() | |
set dataList to my fetchEventsStarting:dateRangeStart ending:dateRangeEnd | |
set eventText to getDaysEvents(dataList) | |
end main | |
-- Shane's handler to get the event data using his library, modified for special handling of all-day and multi-day events. | |
on fetchEventsStarting:dateRangeStart ending:dateRangeEnd | |
-- 'fetch store', 'fetch calendars', and 'fetch events' are from CalendarLib. | |
set theStore to (fetch store) | |
set theCals to (fetch calendars calendarList cal type list {} event store theStore) | |
-- It's come to light that the system method which fetches the events won't get more than four years of them at a time. CalendarLib will no doubt be updated to work round this in time, but here's a work-round anyway. | |
set theEvents to {} | |
set fromDate to dateRangeStart | |
set toDate to fromDate - 1 | |
set justUnderFourYears to (365 * 4) * days | |
repeat until (fromDate comes after dateRangeEnd) | |
set toDate to toDate + justUnderFourYears | |
if (toDate comes after dateRangeEnd) then set toDate to dateRangeEnd | |
set theEvents to theEvents & (fetch events starting date fromDate ending date toDate searching cals theCals event store theStore) | |
set fromDate to fromDate + justUnderFourYears | |
end repeat | |
script o | |
property dataList : {} | |
end script | |
repeat with anEvent in theEvents | |
-- Get the start date, end date, time zone, and summary of each returned event. | |
set {event_start_date:thisStartDate, event_end_date:thisEndDate, event_time_zone:thisTimeZone, event_summary:thisSummary} to (event info for event anEvent) -- CalendarLib. | |
-- If a returned event starts before the date range entered by the user, note the fact and set its start date to that of the range. | |
set startedBeforePeriod to (thisStartDate comes before dateRangeStart) | |
if (startedBeforePeriod) then set thisStartDate to dateRangeStart | |
-- With an all-day event (no associated time zone), nudge its end time forward by a second to 23:59:59 on the event day. | |
set allDayEvent to (thisTimeZone is missing value) | |
if (allDayEvent) then set thisEndDate to thisEndDate - 1 | |
-- Store the start date, 'already started' flag, end date, 'all-day event' flag, and summary. If a multi-day event, add discrete entries for each date it occupies in the range. | |
repeat until ((thisStartDate comes after thisEndDate) or (thisStartDate comes after dateRangeEnd)) | |
set end of o's dataList to {thisStartDate, startedBeforePeriod, thisEndDate, allDayEvent, thisSummary} | |
set thisStartDate to thisStartDate + days | |
set thisStartDate's time to 0 | |
set startedBeforePeriod to true | |
end repeat | |
end repeat | |
-- Sort the entries by start date. | |
sortByStartDate(o's dataList) | |
return o's dataList | |
end fetchEventsStarting:ending: | |
-- Sort the filtered data by start date. | |
on sortByStartDate(dataList) | |
-- Comparison object for a customisable sort. Compares the first items of two passed lists. | |
script byFirstListItem | |
on isGreater(a, b) | |
return (beginning of a > beginning of b) | |
end isGreater | |
end script | |
CustomInsertionSort(dataList, 1, -1, {comparer:byFirstListItem}) | |
end sortByStartDate | |
-- Customisable insertion sort. Algorithm: unknown author. AppleScript implementation: Arthur J. Knapp and Nigel Garvey, 2003. Revised by NG, 2010. | |
on CustomInsertionSort(theList, l, r, customiser) | |
script o | |
property comparer : me | |
property slave : me | |
property lst : theList | |
on isrt(l, r) | |
set u to item l of o's lst | |
repeat with j from (l + 1) to r | |
set v to item j of o's lst | |
if (comparer's isGreater(u, v)) then | |
set here to l | |
set item j of o's lst to u | |
repeat with i from (j - 2) to l by -1 | |
tell item i of o's lst | |
if (comparer's isGreater(it, v)) then | |
set item (i + 1) of o's lst to it | |
else | |
set here to i + 1 | |
exit repeat | |
end if | |
end tell | |
end repeat | |
set item here of o's lst to v | |
slave's rotate(here, j) | |
else | |
set u to v | |
end if | |
end repeat | |
end isrt | |
on isGreater(a, b) | |
(a > b) | |
end isGreater | |
on rotate(a, b) | |
end rotate | |
end script | |
set listLen to (count theList) | |
if (listLen > 1) then | |
if (l < 0) then set l to listLen + l + 1 | |
if (r < 0) then set r to listLen + r + 1 | |
if (l > r) then set {l, r} to {r, l} | |
if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o}) | |
o's isrt(l, r) | |
end if | |
return -- nothing. | |
end CustomInsertionSort | |
-- Ask the user for the range of dates to be covered. | |
on getDateRange() | |
set today to (current date) | |
set dateRangeStart to today | |
set dateRangeStart's time to 0 | |
set dateRangeEnd to today | |
set dateRangeEnd's time to days - 1 -- Sets the last date's time to 23:59:59, the last second of the range. | |
return {dateRangeStart, dateRangeEnd} | |
end getDateRange | |
-- Derive a text from the gathered data. | |
on getDaysEvents(dataList) | |
-- dataList = list of {{thisStartDate, startedBeforePeriod, thisEndDate, allDayEvent, thisSummary}, {thisStartDate, … | |
if (dataList is {}) then | |
return "" #"No events found in this period." | |
else | |
script o | |
property workList : dataList | |
end script | |
repeat with i from 1 to (count dataList) | |
-- Get the data for an event/day from the list of filtered data. | |
set {{date string:thisCalendarStartDate, hours:thisStartTimeH, minutes:thisStartTimeM}, startedBeforeToday, {date string:thisCalendarEndDate, hours:thisEndTimeH, minutes:thisEndTimeM}, allDayEvent, thisSummary} to item i of o's workList | |
set endsAfterToday to (thisCalendarEndDate is not thisCalendarStartDate) | |
-- Begin the entry for the event/day with a suitable expression of its start date. | |
set thisEntry to thisSummary | |
-- Store this entry. | |
set item i of o's workList to thisEntry | |
end repeat | |
-- When all the entries are ready, coerce to a single text with linefeeds and return the result. | |
set astid to AppleScript's text item delimiters | |
set AppleScript's text item delimiters to linefeed | |
set outputText to o's workList as text | |
set AppleScript's text item delimiters to astid | |
return outputText | |
end if | |
end getDaysEvents | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The variable Current_Temp is not defined;
I need remove Current_Temp this.
That's Great. Thanks for idea.