Logic Machine Forum
Automatic holidays and weekends schedule - Printable Version

+- Logic Machine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Automatic holidays and weekends schedule (/showthread.php?tid=3195)



Automatic holidays and weekends schedule - myg - 25.02.2021

I'm sharing this script for a purpose of using "do not run on holidays" exception in the scheduler. The main concern was on how to obtain proper schedule of all weekends and holidays for the year without having to enter them manually. This script does that using 3rd party service. 
It can be configured with the country code, and the best way to use it is to create it as a scheduled script to run once a year on the date of your choice (for ex. middle of December). 
Note that it will clean up existing holidays schedule! Check if service returns correct schedule for your country before use.

Code:
-- Fills in holiday schedule for this and next years
-- License: do whatever you want

local http = require('socket.http')
local ltn12 = require('ltn12')
local country_code = 'ru'

function get_holidays(year)
  local url = string.format('https://isdayoff.ru/api/getdata?cc=%s&year=%s', country_code, year)

  local tbl = {}
  local status, code = http.request({
      url = url,
      sink = ltn12.sink.table(tbl)
    })
 
  if(status) then
    return table.concat(tbl)
  else
    error(string.format('Failed to get holidays for the year: status(%s), code(%s)', status, code))
    return ''
  end
end


function day_to_date(year, day)
  return os.date('*t', os.time({year=year, month=1, day=day}))
end

function holiday_iterator(year, data)
  local workday = string.byte('0')
  local len = #data
  local i = 0
  return function()
    while(i < len) do
      i = i + 1
      if(data:byte(i) ~= workday) then
        return day_to_date(year, i)
      end
    end
  end
end

function create_record(date, duration)
  local name = string.format('%s.%02d.%02d', date.year, date.month, date.day)
  if(duration > 1) then
    local nd = day_to_date(date.year, date.yday + duration - 1)
    name = string.format('%s-%02d.%02d', name, nd.month, nd.day)
  end
  if(date.wday == 7) then
    name = string.format('weekend (%s)', name)
  end

  return {
    name = name,
    year = date.year,
    month = date.month,
    day = date.day,
    duration = duration,
  }
end

function holiday_group_iterator(year, data)
  local accum = nil
  local iter = holiday_iterator(year, data)

  return function()
    local res = nil

    repeat
      local date = iter()

      if(date and accum and date.yday == (accum.date.yday + accum.duration)) then
        -- group if we can
        accum.duration = accum.duration + 1
      else
        -- return result and restart accumulator
        res = accum
        accum = {
          date = date,
          duration = 1,
        }
      end

      if(res and res.date) then
        return create_record(res.date, res.duration)
      end
    until accum == nil or accum.date == nil
  end
end

function delete_holidays()
  db:query('DELETE FROM scheduler_holidays')
end

function create_holidays(year)
  for record in holiday_group_iterator(year, get_holidays(year)) do
    db:insert('scheduler_holidays', record)
    --log('inserting', record.name)
  end
end

delete_holidays()

local this_year = os.date('*t').year
create_holidays(this_year)
create_holidays(this_year + 1)