add utils
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// This is the settings.json mount
|
// This is the settings.json mount
|
||||||
"mounts": ["source=/c/temp/settings_test.json,target=/boot/settings.json,type=bind,consistency=cached"],
|
"mounts": ["source=/mnt/c/temp/settings_test.json,target=/boot/settings.json,type=bind,consistency=cached"],
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
"postCreateCommand": "dos2unix ./.devcontainer/postCreate.sh && chmod +x ./.devcontainer/postCreate.sh && ./.devcontainer/postCreate.sh",
|
"postCreateCommand": "dos2unix ./.devcontainer/postCreate.sh && chmod +x ./.devcontainer/postCreate.sh && ./.devcontainer/postCreate.sh",
|
||||||
|
|||||||
@@ -40,4 +40,4 @@ async def clear_display():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(run())
|
asyncio.run(dry_run())
|
||||||
|
|||||||
@@ -12,6 +12,33 @@ from inkycal.modules.template import inkycal_module
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class TaskEntry:
|
||||||
|
"""Class representing a task entry."""
|
||||||
|
|
||||||
|
def __init__(self, title, project=None, parent_project=None, subtasks=None):
|
||||||
|
self.title = title
|
||||||
|
self.project = project
|
||||||
|
self.consumed_time = 0 # in minutes
|
||||||
|
self.subtasks = []
|
||||||
|
def add_subtask(self, subtask):
|
||||||
|
"""Add a subtask to the task entry."""
|
||||||
|
self.subtasks.append(subtask)
|
||||||
|
def mock_task_list():
|
||||||
|
"""Generate a mock task list for testing purposes."""
|
||||||
|
finetune = TaskEntry("3 new models finetune work", project="AISentry")
|
||||||
|
generate_data = TaskEntry("Generate training data", project="AISentry")
|
||||||
|
function_development = TaskEntry("Function development", project="AISentry")
|
||||||
|
finetune.add_subtask(generate_data)
|
||||||
|
finetune.add_subtask(function_development)
|
||||||
|
|
||||||
|
check_llama = TaskEntry("Check Llama model performance", project="llama.cpp")
|
||||||
|
transform = TaskEntry("Transformers library exploration", project="llama.cpp")
|
||||||
|
check_llama.add_subtask(transform)
|
||||||
|
|
||||||
|
research_work = TaskEntry("Research new AI techniques", project="AISentry")
|
||||||
|
meeting = TaskEntry("Team meeting", project="General")
|
||||||
|
return [finetune, check_llama, research_work, meeting]
|
||||||
|
|
||||||
def get_ip_address():
|
def get_ip_address():
|
||||||
"""Get public IP address from external service."""
|
"""Get public IP address from external service."""
|
||||||
try:
|
try:
|
||||||
@@ -97,13 +124,36 @@ class Today(inkycal_module):
|
|||||||
|
|
||||||
# additional configuration
|
# additional configuration
|
||||||
self.timezone = get_system_tz()
|
self.timezone = get_system_tz()
|
||||||
|
|
||||||
|
# 选择字体:优先使用支持中文的 NotoSansCJK,否则使用 NotoSans
|
||||||
|
self._font_family = self._select_font_family()
|
||||||
self.num_font = ImageFont.truetype(
|
self.num_font = ImageFont.truetype(
|
||||||
fonts['NotoSans-SemiCondensed'], size=self.fontsize
|
fonts[self._font_family], size=self.fontsize
|
||||||
)
|
)
|
||||||
|
|
||||||
# give an OK message
|
# give an OK message
|
||||||
logger.debug(f'{__name__} loaded')
|
logger.debug(f'{__name__} loaded')
|
||||||
|
|
||||||
|
def _select_font_family(self) -> str:
|
||||||
|
"""选择合适的字体族(支持中文优先)"""
|
||||||
|
# 优先级:NotoSansCJKsc (支持中文) > NotoSans
|
||||||
|
preferred_fonts = [
|
||||||
|
'NotoSansCJKsc-Regular',
|
||||||
|
'NotoSans-SemiCondensed'
|
||||||
|
]
|
||||||
|
|
||||||
|
for font_name in preferred_fonts:
|
||||||
|
if font_name in fonts:
|
||||||
|
logger.debug(f'Selected font: {font_name}')
|
||||||
|
return font_name
|
||||||
|
|
||||||
|
# 如果都不存在,使用第一个可用字体
|
||||||
|
return list(fonts.keys())[0]
|
||||||
|
|
||||||
|
def _get_font(self, size: int) -> ImageFont.FreeTypeFont:
|
||||||
|
"""获取指定大小的字体"""
|
||||||
|
return ImageFont.truetype(fonts[self._font_family], size=size)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def flatten(values):
|
def flatten(values):
|
||||||
"""Flatten the values."""
|
"""Flatten the values."""
|
||||||
@@ -143,9 +193,7 @@ class Today(inkycal_module):
|
|||||||
# Edit left section - show today's date
|
# Edit left section - show today's date
|
||||||
now = arrow.now(tz=self.timezone)
|
now = arrow.now(tz=self.timezone)
|
||||||
month_height = int(im_height * 0.15)
|
month_height = int(im_height * 0.15)
|
||||||
month_font = ImageFont.truetype(
|
month_font = self._get_font(int(self.fontsize * 1.5))
|
||||||
fonts['NotoSans-SemiCondensed'], size=int(self.fontsize * 1.5)
|
|
||||||
)
|
|
||||||
write(
|
write(
|
||||||
im_black,
|
im_black,
|
||||||
(0, 0),
|
(0, 0),
|
||||||
@@ -157,9 +205,7 @@ class Today(inkycal_module):
|
|||||||
|
|
||||||
date_height = int(im_height * 0.5)
|
date_height = int(im_height * 0.5)
|
||||||
date_y = month_height
|
date_y = month_height
|
||||||
large_font = ImageFont.truetype(
|
large_font = self._get_font(int(self.fontsize * 4))
|
||||||
fonts['NotoSans-SemiCondensed'], size=int(self.fontsize * 4)
|
|
||||||
)
|
|
||||||
date_time = arrow.now()
|
date_time = arrow.now()
|
||||||
day = date_time.day
|
day = date_time.day
|
||||||
print(str(day))
|
print(str(day))
|
||||||
@@ -174,9 +220,7 @@ class Today(inkycal_module):
|
|||||||
|
|
||||||
weekday_y = month_height + date_height
|
weekday_y = month_height + date_height
|
||||||
weekday_height = int(im_height * 0.15)
|
weekday_height = int(im_height * 0.15)
|
||||||
weekday_font = ImageFont.truetype(
|
weekday_font = self._get_font(int(self.fontsize * 2))
|
||||||
fonts['NotoSans-SemiCondensed'], size=int(self.fontsize * 2)
|
|
||||||
)
|
|
||||||
write(
|
write(
|
||||||
im_black,
|
im_black,
|
||||||
(0, weekday_y),
|
(0, weekday_y),
|
||||||
@@ -214,19 +258,7 @@ class Today(inkycal_module):
|
|||||||
# Edit right section - show today's events
|
# Edit right section - show today's events
|
||||||
if self.show_events:
|
if self.show_events:
|
||||||
# 导入日历解析器
|
# 导入日历解析器
|
||||||
from inkycal.modules.ical_parser import iCalendar
|
upcoming_events = True
|
||||||
|
|
||||||
parser = iCalendar()
|
|
||||||
|
|
||||||
if self.ical_urls:
|
|
||||||
parser.load_url(self.ical_urls)
|
|
||||||
if self.ical_files:
|
|
||||||
parser.load_from_file(self.ical_files)
|
|
||||||
|
|
||||||
# 获取今天的事件
|
|
||||||
today_start = now.floor('day')
|
|
||||||
today_end = now.ceil('day')
|
|
||||||
upcoming_events = parser.get_events(today_start, today_end, self.timezone)
|
|
||||||
|
|
||||||
# 计算右侧可用空间
|
# 计算右侧可用空间
|
||||||
right_x = left_section_width + 5 # 留5px边距
|
right_x = left_section_width + 5 # 留5px边距
|
||||||
@@ -238,42 +270,60 @@ class Today(inkycal_module):
|
|||||||
line_height = text_bbox[3] + line_spacing
|
line_height = text_bbox[3] + line_spacing
|
||||||
max_lines = im_height // line_height
|
max_lines = im_height // line_height
|
||||||
|
|
||||||
|
from inkycal.modules.super_productivity_utils import get_today_tasks
|
||||||
|
|
||||||
|
|
||||||
|
json_file_path = '/workspaces/Inkycal/inkycal/modules/super_productivity.json'
|
||||||
|
task_list = get_today_tasks(json_file_path)
|
||||||
|
|
||||||
if upcoming_events:
|
if upcoming_events:
|
||||||
# 显示事件
|
# Split to 2 parts
|
||||||
cursor = 0
|
# Left part: title
|
||||||
for event in upcoming_events[:max_lines]:
|
# Right part: Project name
|
||||||
if cursor >= max_lines:
|
current_line = 0
|
||||||
break
|
for idx, event in enumerate(task_list):
|
||||||
|
if current_line >= max_lines:
|
||||||
y_pos = cursor * line_height
|
break # 超出显示范围,停止绘制
|
||||||
|
|
||||||
# 显示时间
|
|
||||||
time_str = event['begin'].format(self.time_format, locale=self.language)
|
|
||||||
time_width = int(self.font.getlength(time_str) * 1.1)
|
|
||||||
|
|
||||||
|
# 写任务标题
|
||||||
write(
|
write(
|
||||||
im_black,
|
im_black,
|
||||||
(right_x, y_pos),
|
(right_x, current_line * line_height),
|
||||||
(time_width, line_height),
|
(int(right_usable_width * 0.7), line_height),
|
||||||
time_str,
|
event.title,
|
||||||
font=self.font,
|
font=self.font,
|
||||||
alignment='left'
|
alignment='left'
|
||||||
)
|
)
|
||||||
|
|
||||||
# 显示事件标题
|
# 写项目名称
|
||||||
event_x = right_x + time_width + 5
|
project_name = event.project_name if event.project_name else "Inbox"
|
||||||
event_width = right_usable_width - time_width - 5
|
|
||||||
|
|
||||||
|
write(
|
||||||
|
im_colour,
|
||||||
|
(right_x + int(right_usable_width * 0.7), current_line * line_height),
|
||||||
|
(int(right_usable_width * 0.3), line_height),
|
||||||
|
project_name,
|
||||||
|
font=self.font,
|
||||||
|
alignment='right'
|
||||||
|
)
|
||||||
|
current_line += 1
|
||||||
|
if event.subtasks:
|
||||||
|
for sub_idx, subtask in enumerate(event.subtasks):
|
||||||
|
if subtask.is_done:
|
||||||
|
continue
|
||||||
|
if current_line + sub_idx + 1 >= max_lines:
|
||||||
|
break # 超出显示范围,停止绘制
|
||||||
|
# 写子任务标题,缩进显示
|
||||||
write(
|
write(
|
||||||
im_black,
|
im_black,
|
||||||
(event_x, y_pos),
|
(right_x + 10, current_line * line_height),
|
||||||
(event_width, line_height),
|
(int(right_usable_width * 0.7) - 10, line_height),
|
||||||
event['title'],
|
f"- {subtask.title}",
|
||||||
font=self.font,
|
font=self.font,
|
||||||
alignment='left'
|
alignment='left'
|
||||||
)
|
)
|
||||||
|
current_line += 1 # 更新主循环的索引
|
||||||
cursor += 1
|
pass
|
||||||
else:
|
else:
|
||||||
# 没有事件时显示提示
|
# 没有事件时显示提示
|
||||||
write(
|
write(
|
||||||
|
|||||||
241
inkycal/modules/super_productivity_utils.py
Normal file
241
inkycal/modules/super_productivity_utils.py
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
"""
|
||||||
|
Super Productivity 数据解析工具
|
||||||
|
用于从 Super Productivity 导出的 JSON 中提取任务信息
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from datetime import date
|
||||||
|
from typing import List, Dict, Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class TaskEntry:
|
||||||
|
"""任务条目类"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
task_id: str,
|
||||||
|
title: str,
|
||||||
|
project_name: str,
|
||||||
|
subtasks: Optional[List['TaskEntry']] = None,
|
||||||
|
is_done: bool = False,
|
||||||
|
time_spent: int = 0
|
||||||
|
):
|
||||||
|
self.id = task_id
|
||||||
|
self.title = title
|
||||||
|
self.project_name = project_name
|
||||||
|
self.subtasks = subtasks or []
|
||||||
|
self.is_done = is_done
|
||||||
|
self.time_spent = time_spent # 单位:毫秒
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"TaskEntry(title='{self.title}', project='{self.project_name}', subtasks={len(self.subtasks)})"
|
||||||
|
|
||||||
|
def get_time_spent_hours(self) -> float:
|
||||||
|
"""获取花费的时间(小时)"""
|
||||||
|
return self.time_spent / 3600000.0
|
||||||
|
|
||||||
|
|
||||||
|
def parse_super_productivity_json(json_file_path: str) -> Tuple[Dict, Dict, Dict]:
|
||||||
|
"""
|
||||||
|
解析 Super Productivity JSON 文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_file_path: JSON 文件路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(tasks, projects, tags) 元组
|
||||||
|
"""
|
||||||
|
with open(json_file_path, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
tasks = data['mainModelData']['task']['entities']
|
||||||
|
projects = data['mainModelData']['project']['entities']
|
||||||
|
tags = data['mainModelData']['tag']['entities']
|
||||||
|
|
||||||
|
return tasks, projects, tags
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_name(projects: Dict, project_id: str) -> str:
|
||||||
|
"""获取项目名称"""
|
||||||
|
if project_id in projects:
|
||||||
|
return projects[project_id]['title']
|
||||||
|
return "Inbox"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_task_entry(
|
||||||
|
task_id: str,
|
||||||
|
tasks: Dict,
|
||||||
|
projects: Dict
|
||||||
|
) -> Optional[TaskEntry]:
|
||||||
|
"""
|
||||||
|
解析单个任务条目
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task_id: 任务 ID
|
||||||
|
tasks: 所有任务字典
|
||||||
|
projects: 所有项目字典
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TaskEntry 对象或 None
|
||||||
|
"""
|
||||||
|
task = tasks.get(task_id)
|
||||||
|
if not task:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取基本信息
|
||||||
|
title = task.get('title', 'Untitled')
|
||||||
|
project_id = task.get('projectId', 'INBOX_PROJECT')
|
||||||
|
project_name = get_project_name(projects, project_id)
|
||||||
|
is_done = task.get('isDone', False)
|
||||||
|
time_spent = task.get('timeSpent', 0)
|
||||||
|
|
||||||
|
# 解析子任务
|
||||||
|
subtask_ids = task.get('subTaskIds', [])
|
||||||
|
subtasks = []
|
||||||
|
for subtask_id in subtask_ids:
|
||||||
|
subtask = parse_task_entry(subtask_id, tasks, projects)
|
||||||
|
if subtask:
|
||||||
|
subtasks.append(subtask)
|
||||||
|
|
||||||
|
return TaskEntry(
|
||||||
|
task_id=task_id,
|
||||||
|
title=title,
|
||||||
|
project_name=project_name,
|
||||||
|
subtasks=subtasks,
|
||||||
|
is_done=is_done,
|
||||||
|
time_spent=time_spent
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_today_tasks(
|
||||||
|
json_file_path: str,
|
||||||
|
target_date: Optional[date] = None,
|
||||||
|
include_done: bool = False
|
||||||
|
) -> List[TaskEntry]:
|
||||||
|
"""
|
||||||
|
获取今天 due 的任务
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_file_path: JSON 文件路径
|
||||||
|
target_date: 目标日期,默认为今天
|
||||||
|
include_done: 是否包含已完成的任务
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
今天的任务列表
|
||||||
|
"""
|
||||||
|
if target_date is None:
|
||||||
|
target_date = date.today()
|
||||||
|
|
||||||
|
today_str = target_date.strftime('%Y-%m-%d')
|
||||||
|
tasks, projects, _ = parse_super_productivity_json(json_file_path)
|
||||||
|
|
||||||
|
today_tasks = []
|
||||||
|
for task_id, task in tasks.items():
|
||||||
|
# 检查是否是今天的任务
|
||||||
|
due_day = task.get('dueDay')
|
||||||
|
# print(f'Task ID: {task_id}, Due Day: {due_day}, Today: {today_str}') # 调试输出
|
||||||
|
if due_day != today_str:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查是否已完成
|
||||||
|
if not include_done and task.get('isDone', False):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 只添加顶层任务(没有 parentId 的)
|
||||||
|
if not task.get('parentId'):
|
||||||
|
parsed_task = parse_task_entry(task_id, tasks, projects)
|
||||||
|
if parsed_task:
|
||||||
|
today_tasks.append(parsed_task)
|
||||||
|
|
||||||
|
return today_tasks
|
||||||
|
|
||||||
|
|
||||||
|
def get_tasks_by_tag(
|
||||||
|
json_file_path: str,
|
||||||
|
tag_name: str,
|
||||||
|
include_done: bool = False
|
||||||
|
) -> List[TaskEntry]:
|
||||||
|
"""
|
||||||
|
根据标签获取任务
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_file_path: JSON 文件路径
|
||||||
|
tag_name: 标签名称(如 "TODAY")
|
||||||
|
include_done: 是否包含已完成的任务
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
带有该标签的任务列表
|
||||||
|
"""
|
||||||
|
tasks, projects, tags = parse_super_productivity_json(json_file_path)
|
||||||
|
|
||||||
|
# 查找标签 ID
|
||||||
|
tag_id = None
|
||||||
|
for tid, tag in tags.items():
|
||||||
|
if tag.get('title') == tag_name:
|
||||||
|
tag_id = tid
|
||||||
|
break
|
||||||
|
|
||||||
|
if not tag_id:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 获取带有该标签的任务
|
||||||
|
task_ids = tags[tag_id].get('taskIds', [])
|
||||||
|
result_tasks = []
|
||||||
|
|
||||||
|
for task_id in task_ids:
|
||||||
|
task = tasks.get(task_id)
|
||||||
|
if not task:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查是否已完成
|
||||||
|
if not include_done and task.get('isDone', False):
|
||||||
|
continue
|
||||||
|
|
||||||
|
parsed_task = parse_task_entry(task_id, tasks, projects)
|
||||||
|
if parsed_task:
|
||||||
|
result_tasks.append(parsed_task)
|
||||||
|
|
||||||
|
return result_tasks
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
"""测试代码"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python super_productivity_utils.py <json_file_path>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
json_file = sys.argv[1]
|
||||||
|
|
||||||
|
# 获取今天的任务
|
||||||
|
print("=" * 60)
|
||||||
|
print("📅 TODAY'S TASKS (with due date)")
|
||||||
|
print("=" * 60)
|
||||||
|
today_tasks = get_today_tasks(json_file)
|
||||||
|
|
||||||
|
for task in today_tasks:
|
||||||
|
print(f"\n📋 {task.title}")
|
||||||
|
print(f" 📁 Project: {task.project_name}")
|
||||||
|
|
||||||
|
if task.subtasks:
|
||||||
|
print(f" └─ Subtasks ({len(task.subtasks)}):")
|
||||||
|
for subtask in task.subtasks:
|
||||||
|
status = "✓" if subtask.is_done else "○"
|
||||||
|
print(f" {status} {subtask.title}")
|
||||||
|
|
||||||
|
# 获取 TODAY 标签的任务
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("🏷️ TASKS WITH 'TODAY' TAG")
|
||||||
|
print("=" * 60)
|
||||||
|
tagged_tasks = get_tasks_by_tag(json_file, "TODAY")
|
||||||
|
|
||||||
|
for task in tagged_tasks:
|
||||||
|
print(f"\n📋 {task.title}")
|
||||||
|
print(f" 📁 Project: {task.project_name}")
|
||||||
|
if task.time_spent > 0:
|
||||||
|
print(f" ⏱️ Time spent: {task.get_time_spent_hours():.1f}h")
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print(f"📊 Summary: {len(today_tasks)} due today, {len(tagged_tasks)} tagged TODAY")
|
||||||
|
print("=" * 60)
|
||||||
@@ -26,7 +26,8 @@ class inkycal_module(metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
self.fontsize = conf["fontsize"]
|
self.fontsize = conf["fontsize"]
|
||||||
self.font = ImageFont.truetype(
|
self.font = ImageFont.truetype(
|
||||||
fonts['NotoSansUI-Regular'], size=self.fontsize)
|
fonts['NotoSansCJKsc-Regular'], size=self.fontsize)
|
||||||
|
# fonts['NotoSansUI-SemiCondensed'], size=self.fontsize)
|
||||||
|
|
||||||
def set(self, help=False, **kwargs):
|
def set(self, help=False, **kwargs):
|
||||||
"""Set attributes of class, e.g. class.set(key=value)
|
"""Set attributes of class, e.g. class.set(key=value)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": 2,
|
"position": 2,
|
||||||
"name": "Calendar",
|
"name": "Today",
|
||||||
"config": {
|
"config": {
|
||||||
"size": [
|
"size": [
|
||||||
528,
|
528,
|
||||||
|
|||||||
@@ -564,6 +564,10 @@ def list_supported_displays():
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""
|
||||||
|
$ Main function to run display tests
|
||||||
|
$ python test_display.py --model epd_7_in_5_colour --test all
|
||||||
|
"""
|
||||||
"""Main function to run display tests"""
|
"""Main function to run display tests"""
|
||||||
parser = argparse.ArgumentParser(description='Universal E-Paper Display Test Script')
|
parser = argparse.ArgumentParser(description='Universal E-Paper Display Test Script')
|
||||||
parser.add_argument('--model', type=str, default=None,
|
parser.add_argument('--model', type=str, default=None,
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ tests = [
|
|||||||
"ical_urls": sample_url,
|
"ical_urls": sample_url,
|
||||||
"ical_files": None,
|
"ical_files": None,
|
||||||
"date_format": "D MMM", "time_format": "HH:mm",
|
"date_format": "D MMM", "time_format": "HH:mm",
|
||||||
"padding_x": 10, "padding_y": 10, "fontsize": 12, "language": "en"
|
"padding_x": 10, "padding_y": 10, "fontsize": 14, "language": "zh",
|
||||||
|
"font": "NotoSansCJKsc-Regular",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user