Initial commit
This commit is contained in:
469
calendario_dei_santi.py
Executable file
469
calendario_dei_santi.py
Executable file
@@ -0,0 +1,469 @@
|
||||
#! /usr/bin/python
|
||||
'''
|
||||
Copyright (C) 2017 Artem Synytsyn a.synytsyn@gmail.com
|
||||
|
||||
#TODO: Code cleaning and refactoring
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
'''
|
||||
|
||||
import inkex
|
||||
import sys
|
||||
from lxml import etree
|
||||
from math import *
|
||||
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
import wikipedia
|
||||
import openpyxl
|
||||
|
||||
|
||||
import math, decimal, datetime
|
||||
dec = decimal.Decimal
|
||||
|
||||
|
||||
## __ ___ _ _ ____ _ _
|
||||
## \ \ / (_) | _(_) ___| __ _(_)_ __ | |_ ___
|
||||
## \ \ /\ / /| | |/ / \___ \ / _` | | '_ \| __/ __|
|
||||
## \ V V / | | <| |___) | (_| | | | | | |_\__ \
|
||||
## \_/\_/ |_|_|\_\_|____/ \__,_|_|_| |_|\__|___/
|
||||
##
|
||||
|
||||
|
||||
class WikiSaints:
|
||||
|
||||
def __init__(self, get_from_wiki=False):
|
||||
if get_from_wiki:
|
||||
self.saints_per_month = self.get_data()
|
||||
else:
|
||||
self.saints_per_month = {}
|
||||
self.it_month_name = [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"]
|
||||
|
||||
|
||||
def get_data(self):
|
||||
wikipedia.set_lang("it")
|
||||
lines = wikipedia.page('Calendario_dei_santi').content.split('\n')
|
||||
# print(lines)
|
||||
reg = re.compile(r'(==)*(==)')
|
||||
cat = {}
|
||||
month_name = self.it_month_name
|
||||
month_strg = [ "== "+m+" ==" for m in month_name]
|
||||
d = None
|
||||
for n,l in enumerate(lines):
|
||||
if reg.search(l):
|
||||
cat[l] = []
|
||||
d = cat[l]
|
||||
elif d is not None:
|
||||
d.append(l)
|
||||
|
||||
clean_cat = {}
|
||||
for m in month_name:
|
||||
clean_cat[m] = cat[f"== {m} =="]
|
||||
return clean_cat
|
||||
|
||||
def save(self, file_name):
|
||||
wb = openpyxl.Workbook(file_name)
|
||||
|
||||
for k,data in self.saints_per_month.items():
|
||||
ws = wb.create_sheet(k)
|
||||
# add column headings. NB. these must be strings
|
||||
ws.append(["Giorno", "Santo", "altri santi"])
|
||||
for i,s in enumerate(data):
|
||||
try:
|
||||
g = s.split(":")[0]
|
||||
S = s.split(":")[1].split(";")[0]
|
||||
o = s.split(":")[1].split(";")[1]
|
||||
except:
|
||||
g = S = o = ""
|
||||
ws.append([i+1, S, o])
|
||||
|
||||
wb.save(file_name)
|
||||
|
||||
def open(self, file_name):
|
||||
wb = openpyxl.open(file_name)
|
||||
|
||||
for ws in wb.worksheets:
|
||||
self.saints_per_month[ws.title] = []
|
||||
for i,row in enumerate(ws.rows):
|
||||
if i==0: continue # skip header
|
||||
elements = [ el.value for el in row][1:]
|
||||
if len(elements) and elements[0] is not None:
|
||||
self.saints_per_month[ws.title].append(elements)
|
||||
|
||||
def print_month(self, month: str):
|
||||
try:
|
||||
m = self.saints_per_month[month]
|
||||
for i,el in enumerate(m):
|
||||
print(i+1, el)
|
||||
except:
|
||||
pass
|
||||
|
||||
def get_saint(self, month : str, day: int):
|
||||
try:
|
||||
return self.saints_per_month[month][day-1]
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_date(self, day : int, month : str, year : int = None) -> str :
|
||||
if year is None: year = datetime.date.today().year
|
||||
month = self.it_month_name.index(month)
|
||||
return datetime.date(year, month+1, day)
|
||||
|
||||
def get_weekday(self, date) -> str :
|
||||
wday = date.weekday()
|
||||
it_wday = [
|
||||
'Lunedì',
|
||||
'Martedì',
|
||||
'Mercoledì',
|
||||
'Giovedì',
|
||||
'Venerdì',
|
||||
'Sabato',
|
||||
'Domenica'
|
||||
]
|
||||
it_wday_short = ['Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab', 'Dom']
|
||||
return it_wday[wday]
|
||||
|
||||
def get_moon_position(self, date=None):
|
||||
if date is None:
|
||||
date = datetime.date.today()
|
||||
diff = date - datetime.date(2001, 1, 1)
|
||||
days = dec(diff.days) + (dec(diff.seconds) / dec(86400))
|
||||
lunations = dec("0.20439731") + (days * dec("0.03386319269"))
|
||||
return lunations % dec(1)
|
||||
|
||||
def get_moon_phase(self, date=None, phases=4):
|
||||
index = ((self.get_moon_position(date)+dec(0.26))%dec(1) * dec(phases))# + dec("0.5")
|
||||
if index - math.floor(index) < phases/30:
|
||||
index = math.floor(index)
|
||||
return int(index)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _get_easter(self, year):
|
||||
"""
|
||||
Use easter date algorithm to calculate the day of easter for a given year.
|
||||
If the year given is in a special range(special_years) then subtract 7 from the final date.
|
||||
|
||||
return easter as offset from March 1 -> goes in April if day > 31
|
||||
"""
|
||||
|
||||
special_years = ['1954', '1981', '2049', '2076']
|
||||
specyr_sub = 7
|
||||
a = year % 19
|
||||
b = year % 4
|
||||
c = year % 7
|
||||
d = (19 * a + 24) % 30
|
||||
e = ((2 * b) + (4 * c) + (6 * d) + 5) % 7
|
||||
|
||||
if year in special_years:
|
||||
dateofeaster = (22 + d + e) - specyr_sub
|
||||
else:
|
||||
dateofeaster = 22 + d + e
|
||||
return dateofeaster
|
||||
|
||||
def get_mobile_fest(self, date):
|
||||
"""
|
||||
### https://it.cathopedia.org/wiki/Calcolo_delle_feste_mobili_legate_alla_Pasqua
|
||||
"""
|
||||
mobile_fests = {
|
||||
'easter' : "Resurrezione del Signore",
|
||||
'mc' : "Mercoledì delle Ceneri", # -46
|
||||
'dc' : "Domenica delle Palme", # -7
|
||||
'as' : "Ascensione di nostro Signore", # 42
|
||||
'cd' : "Solennità del Corpus Domini", # 63 (dal 1978)
|
||||
'cr' : "Festa del Ctristo Re", # prima di avvento ( metti a mano )
|
||||
}
|
||||
# easter
|
||||
eday = self._get_easter(date.year)
|
||||
emonth = 3
|
||||
if eday > 31:
|
||||
eday -= 31
|
||||
emonth = 4
|
||||
easter = datetime.date(date.year, emonth, eday)
|
||||
if date == easter: return mobile_fests['easter']
|
||||
if date == easter - datetime.timedelta(days=46): return mobile_fests['mc'] # mercoledi delle ceneri
|
||||
if date == easter - datetime.timedelta(days=7): return mobile_fests['dc'] # domenica delle palme
|
||||
if date == easter + datetime.timedelta(days=42): return mobile_fests['as'] # ascensione
|
||||
if date == easter + datetime.timedelta(days=63): return mobile_fests['cd'] # corpus domini
|
||||
|
||||
|
||||
# AVVENTO ( da rifare )
|
||||
# xm = datetime.date(date.year, 12, 25)
|
||||
# a4 = xm - datetime.timedelta(days=7)
|
||||
# a3 = a4 - datetime.timedelta(days=7)
|
||||
# a2 = a3 - datetime.timedelta(days=7)
|
||||
# a1 = a2 - datetime.timedelta(days=7)
|
||||
# if date == a1: return mobile_fests['a1']
|
||||
# if date == a2: return mobile_fests['a2']
|
||||
# if date == a3: return mobile_fests['a3']
|
||||
# if date == a4: return mobile_fests['a4']
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
## ____ _ _ _ ____ _ _
|
||||
## / ___|__ _| | ___ _ __ __| | __ _ _ __(_) ___/ ___| __ _ _ __ | |_(_)
|
||||
## | | / _` | |/ _ \ '_ \ / _` |/ _` | '__| |/ _ \___ \ / _` | '_ \| __| |
|
||||
## | |__| (_| | | __/ | | | (_| | (_| | | | | (_) |__) | (_| | | | | |_| |
|
||||
## \____\__,_|_|\___|_| |_|\__,_|\__,_|_| |_|\___/____/ \__,_|_| |_|\__|_|
|
||||
##
|
||||
|
||||
class _Spacing():
|
||||
size = 0.
|
||||
padding = 0.
|
||||
margin_top = 0.
|
||||
margin_bottom = 0.
|
||||
margin_left = 0.
|
||||
margin_right = 0.
|
||||
|
||||
class CalendarioSanti(inkex.Effect):
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
|
||||
# General settings
|
||||
self.arg_parser.add_argument("--x",
|
||||
type=float,
|
||||
dest="x", default=0.0,
|
||||
help="Center X")
|
||||
self.arg_parser.add_argument("--y",
|
||||
type=float,
|
||||
dest="y", default=0.0,
|
||||
help="Center Y")
|
||||
self.arg_parser.add_argument("--columnwidth",
|
||||
type=float,
|
||||
dest="columnwidth", default=300,
|
||||
help="Calendar Column width")
|
||||
self.arg_parser.add_argument("--linewidth",
|
||||
type=float,
|
||||
dest="linewidth", default=1,
|
||||
help="day separation line width")
|
||||
self.arg_parser.add_argument("--rowheight",
|
||||
type=float,
|
||||
dest="rowheight", default=12,
|
||||
help="row height")
|
||||
self.arg_parser.add_argument("-u", "--units",
|
||||
type=str,
|
||||
dest="units", default="mm",
|
||||
help="units to measure size of knob")
|
||||
|
||||
# Dummy
|
||||
self.arg_parser.add_argument("--tab")
|
||||
|
||||
# Label settings
|
||||
self.arg_parser.add_argument("--saints_file",
|
||||
type=str,
|
||||
dest="saints_file", default='',
|
||||
help="xlsx file to load saints")
|
||||
self.arg_parser.add_argument("--font_family",
|
||||
type=str,
|
||||
dest="font_family", default="Apple Garamond",
|
||||
help="Font family to use for days and labels")
|
||||
self.arg_parser.add_argument("--year",
|
||||
type=int,
|
||||
dest="year", default=1970,
|
||||
help="Calendar Year")
|
||||
self.arg_parser.add_argument("-m", "--month",
|
||||
type=str,
|
||||
dest="month", default="Gennaio",
|
||||
help="month to render")
|
||||
self.arg_parser.add_argument("--dayfrom",
|
||||
type=int,
|
||||
dest="dayfrom", default=1,
|
||||
help="Range to render day from")
|
||||
self.arg_parser.add_argument("--dayto",
|
||||
type=int,
|
||||
dest="dayto", default=31,
|
||||
help="Range to render day to")
|
||||
self.arg_parser.add_argument("--day_text_size",
|
||||
type=float,
|
||||
dest="day_text_size", default=12,
|
||||
help="Text size of day")
|
||||
self.arg_parser.add_argument("--saint_text_size",
|
||||
type=float,
|
||||
dest="saint_text_size", default=8,
|
||||
help="Text size of saint")
|
||||
|
||||
# spacing
|
||||
self.__elements = ['day', 'wday', 'saint', 'moon']
|
||||
self.__spacing = {
|
||||
'size' : 8,
|
||||
'padding' : 0,
|
||||
'margin-top' : 0,
|
||||
'margin-bottom' : 0,
|
||||
'margin-left' : 0,
|
||||
'margin-right' : 0
|
||||
}
|
||||
for el in self.__elements:
|
||||
for sk in self.__spacing.keys():
|
||||
sk = sk.replace('-','_')
|
||||
self.arg_parser.add_argument("--" + el + "_" + sk,
|
||||
type=float,
|
||||
dest=el + "_" + sk,
|
||||
help="Spacing " + el + " " + sk)
|
||||
|
||||
|
||||
|
||||
|
||||
def draw_text(self, textvalue, pos, text_size, parent, bold=False, italic=False ):
|
||||
# Create text element
|
||||
x,y = pos
|
||||
font = str(self.options.font_family)
|
||||
font_specs = str(self.options.font_family)
|
||||
if bold: font_specs += ", Bold"
|
||||
else: font_specs += ", Normal"
|
||||
# Center text horizontally with CSS style.
|
||||
style = {
|
||||
'text-align' : 'right',
|
||||
'text-anchor': 'start',
|
||||
'alignment-baseline' : 'baseline',
|
||||
'font-family' : f"'{font}'",
|
||||
'-inkscape-font-specification' : f"'{font_specs}'",
|
||||
'font-weight' : 'bold' if bold else 'normal',
|
||||
'font-size' : str(text_size),
|
||||
'vertical-align' : 'middle'
|
||||
}
|
||||
|
||||
attribs = {'style' : str(inkex.Style(style)),
|
||||
'x' : str(x), 'y' : str(y), 'text' : str(textvalue) }
|
||||
|
||||
text = etree.SubElement(parent, inkex.addNS('text','svg'), attribs )
|
||||
text.text = textvalue
|
||||
text.set('style', str(inkex.Style(style)))
|
||||
# parent.append(text)
|
||||
return text
|
||||
|
||||
|
||||
def draw_moon_text(self, phase, pos, text_size, parent):
|
||||
moon_phase = "○◑●◐"
|
||||
textvalue = moon_phase[(phase+1)%4]
|
||||
return self.draw_text(textvalue, pos, text_size, parent)
|
||||
|
||||
|
||||
def draw_hline(self, pos, length, parent):
|
||||
x1,y1 = pos
|
||||
x2,y2 = pos[0] + length, pos[1]
|
||||
|
||||
line_style = { 'stroke': '#000000',
|
||||
'stroke-width': str(self.spacing['linewidth']),
|
||||
'fill': 'none'
|
||||
}
|
||||
|
||||
line_attribs = {'style' : str(inkex.Style(line_style)),
|
||||
inkex.addNS('label','inkscape') : "none",
|
||||
'd' : 'M '+str(x1) +',' +
|
||||
str(y1) +' L '+str(x2)
|
||||
+','+str(y2) }
|
||||
|
||||
line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )
|
||||
return line
|
||||
|
||||
|
||||
def draw_day(self, cal_date, pos, parent, fest = False):
|
||||
date = self._wss.get_date(cal_date['day'], cal_date['month'], cal_date['year'])
|
||||
bold = date.weekday() == 6 # Domenica
|
||||
|
||||
# import feste fisse da Excel
|
||||
if cal_date['fest'] != 'None':
|
||||
fest = True
|
||||
saint_textvalue = cal_date['fest'] if fest else str(cal_date['saint'])
|
||||
|
||||
# calcolo feste mobili
|
||||
mobile_fest = self._wss.get_mobile_fest(date)
|
||||
if mobile_fest is not None:
|
||||
fest = True
|
||||
saint_textvalue += " - " + mobile_fest
|
||||
|
||||
if fest: bold = True
|
||||
|
||||
wday = self._wss.get_weekday(date)
|
||||
mphs = self._wss.get_moon_phase(date)
|
||||
day_text = self.draw_text(str(cal_date['day']), pos['day'], self.spacing['day'].size, parent, bold=bold)
|
||||
wdl_text = self.draw_text(str(wday), pos['wday'], self.spacing['wday'].size, parent, bold=bold)
|
||||
ssn_text = self.draw_text(saint_textvalue, pos['saint'], self.spacing['saint'].size, parent, bold=bold)
|
||||
if mphs is not None:
|
||||
mph_text = self.draw_moon_text( mphs, pos['moon'], self.spacing['moon'].size, parent)
|
||||
h_line = self.draw_hline( pos['hline'], self.spacing['columnwidth'],parent)
|
||||
pass
|
||||
|
||||
def _compute_spacing(self, x, y, it):
|
||||
d = self.spacing['day']
|
||||
w = self.spacing['wday']
|
||||
s = self.spacing['saint']
|
||||
m = self.spacing['moon']
|
||||
dbb = 1.5*d.size + 2*d.padding + d.margin_left + d.margin_right, \
|
||||
d.size + 2*d.padding + d.margin_top + d.margin_bottom
|
||||
sby = s.size + 2*s.padding + s.margin_top + s.margin_bottom
|
||||
wby = w.size + 2*w.padding + w.margin_top + w.margin_bottom
|
||||
|
||||
lw = self.spacing['linewidth']
|
||||
cw = self.spacing['columnwidth']
|
||||
rh = self.spacing['rowheight']
|
||||
y = y + max(rh, dbb[1]+lw) * it
|
||||
|
||||
ans = {
|
||||
'day' : (x+ d.padding+d.margin_left , y+ dbb[1]-d.padding-d.margin_bottom),
|
||||
'wday' : (x+ dbb[0] + w.padding+w.margin_left , y+ dbb[1]-sby-w.padding-w.margin_bottom),
|
||||
'saint': (x+ dbb[0] + s.padding+s.margin_left , y+ dbb[1]-s.padding-s.margin_bottom),
|
||||
'hline': (x , y+ dbb[1]),
|
||||
'moon' : (x + cw - m.size-2*m.padding-m.margin_right, y+ m.size+2*m.padding+m.margin_top),
|
||||
}
|
||||
return ans
|
||||
|
||||
def effect(self):
|
||||
|
||||
parent = self.svg.get_current_layer()
|
||||
selected = self.svg.selected
|
||||
|
||||
if selected:
|
||||
bbox = selected.bounding_box()
|
||||
self.x_offset = bbox.left
|
||||
self.y_offset = bbox.top
|
||||
else:
|
||||
self.x_offset = self.svg.unittouu(str(self.options.x) + self.options.units)
|
||||
self.y_offset = self.svg.unittouu(str(self.options.y) + self.options.units)
|
||||
|
||||
# populating spacing with options
|
||||
self.spacing = {}
|
||||
for el in self.__elements:
|
||||
self.spacing[el] = _Spacing()
|
||||
for sk in self.__spacing.keys():
|
||||
sk = sk.replace('-','_')
|
||||
self.spacing[el].__dict__[sk] = self.svg.unittouu( str(getattr(self.options ,el+'_'+sk)) + self.options.units)
|
||||
self.spacing['linewidth'] = getattr(self.options ,'linewidth')
|
||||
self.spacing['columnwidth'] = self.svg.unittouu( str(getattr(self.options ,'columnwidth')) + self.options.units)
|
||||
self.spacing['rowheight'] = self.svg.unittouu( str(getattr(self.options ,'rowheight')) + self.options.units)
|
||||
|
||||
if len(self.options.saints_file):
|
||||
ss = WikiSaints()
|
||||
ss.open(str(self.options.saints_file))
|
||||
else:
|
||||
ss = WikiSaints(get_from_wiki=True)
|
||||
self._wss = ss
|
||||
|
||||
compute_date = lambda d: { 'day' : d, 'month' : self.options.month, 'year': self.options.year,
|
||||
'saint' : str(ss.get_saint(self.options.month, d)[0]), 'fest' : str(ss.get_saint(self.options.month, d)[2]) }
|
||||
for n,d in enumerate(range(self.options.dayfrom, self.options.dayto + 1)):
|
||||
cal_date = compute_date(d)
|
||||
pos = self._compute_spacing(self.x_offset, self.y_offset, n)
|
||||
self.draw_day(cal_date, pos, parent)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
e = CalendarioSanti()
|
||||
e.run()
|
||||
|
||||
Reference in New Issue
Block a user