@@ -1,49 +1,52 @@
#!/bin/python3
#lumbung.space calendar feed generator
#© 2021 roel roscam abbing gplv3 etc
# lumbung.space calendar feed generator
# © 2021 roel roscam abbing gplv3 etc
from ics import Calendar
import requests
import jinja2
import os
import shutil
from slugify import slugify
from natural import date
from event_feed_config import calendar_url , output_dir
from urllib . parse import urlparse
import arrow
import re
import shutil
from urllib . parse import urlparse
import arrow
import jinja2
import requests
from ics import Calendar
from natural import date
from slugify import slugify
from event_feed_config import calendar_url , output_dir
cal = Calendar ( requests . get ( calendar_url ) . text )
env = jinja2 . Environment (
loader = jinja2 . FileSystemLoader ( os . path . curdir )
)
env = jinja2 . Environment ( loader = jinja2 . FileSystemLoader ( os . path . curdir ) )
if not os . path . exists ( output_dir ) :
os . mkdir ( output_dir )
template = env . get_template ( ' event_template.md ' )
template = env . get_template ( " event_template.md " )
existing_posts = os . listdir ( output_dir )
def findURLs ( string ) :
"""
return all URLs in a given string
"""
regex = r " (?i) \ b((?:https?://|www \ d { 0,3}[.]|[a-z0-9. \ -]+[.][a-z] { 2,4}/)(?:[^ \ s()<>]+| \ (([^ \ s()<>]+|( \ ([^ \ s()<>]+ \ )))* \ ))+(?: \ (([^ \ s()<>]+|( \ ([^ \ s()<>]+ \ )))* \ )|[^ \ s`!() \ [ \ ] {} ;: ' \" .,<>?«»“”‘’])) "
url = re . findall ( regex , string )
return [ x [ 0 ] for x in url ]
url = re . findall ( regex , string )
return [ x [ 0 ] for x in url ]
def find_imageURLS ( string ) :
"""
return all image URLS in a given string
"""
regex = r " (?:http \ :|https \ :)? \ / \ /.*? \ .(?:png|jpg|jpeg|gif|svg) "
regex = r " (?:http \ :|https \ :)? \ / \ /.*? \ .(?:png|jpg|jpeg|gif|svg) "
img_urls = re . findall ( regex , string , flags = re . IGNORECASE )
return img_urls
return img_urls
def create_metadata ( event ) :
"""
@@ -55,24 +58,28 @@ def create_metadata(event):
if location_urls :
location_url = location_urls [ 0 ]
event . location = ' [ {} ]( {} ) ' . format ( urlparse ( location_url ) . netloc , location_url )
event . location = " [ {} ]( {} ) " . format (
urlparse ( location_url ) . netloc , location_url
)
event_metadata = {
' name ' : event . name ,
' created ' : event . created . format ( ) ,
' description ' : event . description ,
' localized_begin ' : ' ' . join ( localize_time ( event . begin ) ) , #non-breaking space characters to defeat markdown
' begin ' : event . begin . format ( ) ,
' end ' : event . end . format ( ) ,
' duration ' : date . compress ( event . duration ) ,
' location ' : event . location ,
' uid ' : event . uid ,
' images ' : find_imageURLS ( event . description ) # currently not used in template
" name " : event . name ,
" created " : event . created . format ( ) ,
" description " : event . description ,
" localized_begin " : " " . join (
localize_time ( event . begin )
) , # non-breaking space characters to defeat markdown
" begin " : event . begin . format ( ) ,
" end " : event . end . format ( ) ,
" duration " : date . compress ( event . duration ) ,
" location " : event . location ,
" uid " : event . uid ,
" images " : find_imageURLS ( event . description ) , # currently not used in template
}
return event_metadata
def localize_time ( date ) :
"""
Turn a given date into various timezones
@@ -82,28 +89,27 @@ def localize_time(date):
# 3 PM Kassel, Germany, 4 PM Ramallah/Jerusalem, Palestina (QoF),
# 8 AM Bogota, Colombia (MaMa), 8 PM Jakarta, Indonesia (Gudskul),
# 1 PM (+1day) Wellington, New Zealand (Fafswag), 9 AM Havana, Cuba (Instar).
tzs = [
( ' Kassel ' , ' Europe/Berlin ' ) ,
( ' Bamako ' , ' Europe/London ' ) ,
( ' Palestine ' , ' Asia/Jerusalem ' ) ,
( ' Bogota ' , ' America/Bogota ' ) ,
( ' Jakarta ' , ' Asia/Jakarta ' ) ,
( ' Makassar ' , ' Asia/Makassar ' ) ,
( ' Wellington ' , ' Pacific/Auckland ' )
]
( " Kassel " , " Europe/Berlin " ) ,
( " Bamako " , " Europe/London " ) ,
( " Palestine " , " Asia/Jerusalem " ) ,
( " Bogota " , " America/Bogota " ) ,
( " Jakarta " , " Asia/Jakarta " ) ,
( " Makassar " , " Asia/Makassar " ) ,
( " Wellington " , " Pacific/Auckland " ) ,
]
localized_begins = [ ]
localized_begins = [ ]
for location , tz in tzs :
localized_begins . append ( #javascript formatting because of string creation from hell
' __ {} __ {} ' . format (
str ( location ) ,
str ( date . to ( tz ) . format ( " YYYY-MM-DD __HH:mm__ " ) )
)
localized_begins . append ( # javascript formatting because of string creation from hell
" __ {} __ {} " . format (
str ( location ) , str ( date . to ( tz ) . format ( " YYYY-MM-DD __HH:mm__ " ) )
)
)
return localized_begins
def create_event_post ( post_dir , event ) :
"""
Create HUGO post based on calendar event metadata
@@ -112,83 +118,86 @@ def create_event_post(post_dir, event):
In that case it will also delete images no longer in metadata
TODO: split this up into more functions for legibility
"""
if not os . path . exists ( post_dir ) :
os . mkdir ( post_dir )
event_metadata = create_metadata ( event )
#list already existing images
#so we can later delete them if we dont find them in the event metadata anymore
# list already existing images
# so we can later delete them if we dont find them in the event metadata anymore
existing_images = os . listdir ( post_dir )
try :
existing_images . remove ( ' index.md ' )
existing_images . remove ( ' .timestamp ' )
existing_images . remove ( " index.md " )
existing_images . remove ( " .timestamp " )
except :
pass
for img in event_metadata [ ' images ' ] :
for img in event_metadata [ " images " ] :
# parse img url to safe local image name
img_name = img . split ( " / " ) [ - 1 ]
fn , ext = img_name . split ( " . " )
img_name = slugify ( fn ) + " . " + ext
#parse img url to safe local image name
img_name = img . split ( ' / ' ) [ - 1 ]
fn , ext = img_name . split ( ' . ' )
img_name = slugify ( fn ) + ' . ' + ext
local_image = os . path . join ( post_dir , img_name )
if not os . path . exists ( local_image ) :
#download preview image
# download preview image
response = requests . get ( img , stream = True )
with open ( local_image , ' wb ' ) as img_file :
with open ( local_image , " wb " ) as img_file :
shutil . copyfileobj ( response . raw , img_file )
print ( ' Downloaded image for event " {} " ' . format ( event . name ) )
event_metadata [ ' description ' ] = event_metadata [ ' description ' ] . replace ( img , '  ' . format ( img_name ) )
event_metadata [ " description " ] = event_metadata [ " description " ] . replace (
img , "  " . format ( img_name )
)
if img_name in existing_images :
existing_images . remove ( img_name )
for left_over_image in existing_images :
#remove images we found, but which are no longer in remote event
os . remove ( os . path . join ( post_dir , left_over_image ) )
print ( ' deleted image ' , left_over_image )
# remove images we found, but which are no longer in remote event
os . remove ( os . path . join ( post_dir , left_over_image ) )
print ( " deleted image " , left_over_image )
with open ( os . path . join ( post_dir , ' index.md ' ) , ' w ' ) as f :
post = template . render ( event = event_metadata )
with open ( os . path . join ( post_dir , " index.md " ) , " w " ) as f :
post = template . render ( event = event_metadata )
f . write ( post )
print ( ' created post for ' , event . name , ' ( {} ) ' . format ( event . uid ) )
print ( " created post for " , event . name , " ( {} ) " . format ( event . uid ) )
with open ( os . path . join ( post_dir , ' .timestamp ' ) , ' w ' ) as f :
f . write ( event_metadata [ ' created ' ] )
with open ( os . path . join ( post_dir , " .timestamp " ) , " w " ) as f :
f . write ( event_metadata [ " created " ] )
def update_event_post ( post_dir , event ) :
"""
Update a post based on the VCARD event ' created ' field which changes when updated
"""
"""
if os . path . exists ( post_dir ) :
old_timestamp = open ( os . path . join ( post_dir , ' .timestamp ' ) ) . read ( )
old_timestamp = open ( os . path . join ( post_dir , " .timestamp " ) ) . read ( )
if event . created > arrow . get ( old_timestamp ) :
print ( ' Updating ' , event . name , ' ( {} ) ' . format ( event . uid ) )
print ( " Updating " , event . name , " ( {} ) " . format ( event . uid ) )
create_event_post ( post_dir , event )
else :
print ( ' Event current: ' , event . name , ' ( {} ) ' . format ( event . uid ) )
print ( " Event current: " , event . name , " ( {} ) " . format ( event . uid ) )
for event in list ( cal . events ) :
post_dir = os . path . join ( output_dir , event . uid )
if event . uid not in existing_posts :
#if there is an event we dont already have, make it
if event . uid not in existing_posts :
# if there is an event we dont already have, make it
create_event_post ( post_dir , event )
elif event . uid in existing_posts :
#if we already have it, update
elif event . uid in existing_posts :
# if we already have it, update
update_event_post ( post_dir , event )
existing_posts . remove ( event . uid ) # create list of posts which have not been returned by the calendar
for post in existing_posts :
#remove events not returned by the calendar (deletion)
print ( ' deleted ' , post )
shutil . rmtree ( os . path . join ( output_dir , post ) )
existing_posts . remove (
event . uid
) # create list of posts which have not been returned by the calendar
for post in existing_posts :
# remove events not returned by the calendar (deletion)
print ( " deleted " , post )
shutil . rmtree ( os . path . join ( output_dir , post ) )