使用 Jupyter 改善你的时间管理
创始人
2024-03-02 07:01:09
0

在 Jupyter 里使用 Python 来分析日历,以了解你是如何使用时间的。

Python 在探索数据方面具有令人难以置信的可扩展性。利用 PandasDask,你可以将 Jupyter 扩展到大数据领域。但是小数据、个人资料、私人数据呢?

JupyterLab 和 Jupyter Notebook 为我提供了一个绝佳的环境,可以让我审视我的笔记本电脑生活。

我的探索是基于以下事实:我使用的几乎每个服务都有一个 Web API。我使用了诸多此类服务:待办事项列表、时间跟踪器、习惯跟踪器等。还有一个几乎每个人都会使用到:日历。相同的思路也可以应用于其他服务,但是日历具有一个很酷的功能:几乎所有 Web 日历都支持的开放标准 —— CalDAV。

在 Jupyter 中使用 Python 解析日历

大多数日历提供了导出为 CalDAV 格式的方法。你可能需要某种身份验证才能访问这些私有数据。按照你的服务说明进行操作即可。如何获得凭据取决于你的服务,但是最终,你应该能够将这些凭据存储在文件中。我将我的凭据存储在根目录下的一个名为 .caldav 的文件中:

import os
with open(os.path.expanduser("~/.caldav")) as fpin:
    username, password = fpin.read().split()

切勿将用户名和密码直接放在 Jupyter Notebook 的笔记本中!它们可能会很容易因 git push 的错误而导致泄漏。

下一步是使用方便的 PyPI caldav 库。我找到了我的电子邮件服务的 CalDAV 服务器(你可能有所不同):

import caldav
client = caldav.DAVClient(url="https://caldav.fastmail.com/dav/", username=username, password=password)

CalDAV 有一个称为 principal(主键)的概念。它是什么并不重要,只要知道它是你用来访问日历的东西就行了:

principal = client.principal()
calendars = principal.calendars()

从字面上讲,日历就是关于时间的。访问事件之前,你需要确定一个时间范围。默认一星期就好:

from dateutil import tz
import datetime
now = datetime.datetime.now(tz.tzutc())
since = now - datetime.timedelta(days=7)

大多数人使用的日历不止一个,并且希望所有事件都在一起出现。itertools.chain.from_iterable 方法使这一过程变得简单:

import itertools

raw_events = list(
    itertools.chain.from_iterable(
        calendar.date_search(start=since, end=now, expand=True)
        for calendar in calendars
    )
)

将所有事件读入内存很重要,以 API 原始的本地格式进行操作是重要的实践。这意味着在调整解析、分析和显示代码时,无需返回到 API 服务刷新数据。

但 “原始” 真的是原始,事件是以特定格式的字符串出现的:

print(raw_events[12].data)
    BEGIN:VCALENDAR
    VERSION:2.0
    PRODID:-//CyrusIMAP.org/Cyrus
     3.3.0-232-g4bdb081-fm-20200825.002-g4bdb081a//EN
    BEGIN:VEVENT
    DTEND:20200825T230000Z
    DTSTAMP:20200825T181915Z
    DTSTART:20200825T220000Z
    SUMMARY:Busy
    UID:
     1302728i-040000008200E00074C5B7101A82E00800000000D939773EA578D601000000000
     000000010000000CD71CC3393651B419E9458134FE840F5
    END:VEVENT
    END:VCALENDAR

幸运的是,PyPI 可以再次使用另一个辅助库 vobject 解围:

import io
import vobject

def parse_event(raw_event):
    data = raw_event.data
    parsed = vobject.readOne(io.StringIO(data))
    contents = parsed.vevent.contents
    return contents
parse_event(raw_events[12])
    {'dtend': [],
     'dtstamp': [],
     'dtstart': [],
     'summary': [],
     'uid': []}

好吧,至少好一点了。

仍有一些工作要做,将其转换为合理的 Python 对象。第一步是 拥有 一个合理的 Python 对象。attrs 库提供了一个不错的开始:

import attr
from __future__ import annotations
@attr.s(auto_attribs=True, frozen=True)
class Event:
    start: datetime.datetime
    end: datetime.datetime
    timezone: Any
    summary: str

是时候编写转换代码了!

第一个抽象从解析后的字典中获取值,不需要所有的装饰:

def get_piece(contents, name):
    return contents[name][0].value
get_piece(_, "dtstart")
    datetime.datetime(2020, 8, 25, 22, 0, tzinfo=tzutc())

日历事件总有一个“开始”、有一个“结束”、有一个 “持续时间”。一些谨慎的解析逻辑可以将两者协调为同一个 Python 对象:

def from_calendar_event_and_timezone(event, timezone):
    contents = parse_event(event)
    start = get_piece(contents, "dtstart")
    summary = get_piece(contents, "summary")
    try:
        end = get_piece(contents, "dtend")
    except KeyError:
        end = start + get_piece(contents, "duration")
    return Event(start=start, end=end, summary=summary, timezone=timezone)

将事件放在 本地 时区而不是 UTC 中很有用,因此使用本地时区:

my_timezone = tz.gettz()
from_calendar_event_and_timezone(raw_events[12], my_timezone)
    Event(start=datetime.datetime(2020, 8, 25, 22, 0, tzinfo=tzutc()), end=datetime.datetime(2020, 8, 25, 23, 0, tzinfo=tzutc()), timezone=tzfile('/etc/localtime'), summary='Busy')

既然事件是真实的 Python 对象,那么它们实际上应该具有附加信息。幸运的是,可以将方法添加到类中。

但是要弄清楚哪个事件发生在哪一天不是很直接。你需要在 本地 时区中选择一天:

def day(self):
    offset = self.timezone.utcoffset(self.start)
    fixed = self.start + offset
    return fixed.date()
Event.day = property(day)
print(_.day)
    2020-08-25

事件在内部始终是以“开始”/“结束”的方式表示的,但是持续时间是有用的属性。持续时间也可以添加到现有类中:

def duration(self):
    return self.end - self.start
Event.duration = property(duration)
print(_.duration)
    1:00:00

现在到了将所有事件转换为有用的 Python 对象了:

all_events = [from_calendar_event_and_timezone(raw_event, my_timezone)
              for raw_event in raw_events]

全天事件是一种特例,可能对分析生活没有多大用处。现在,你可以忽略它们:

# ignore all-day events
all_events = [event for event in all_events if not type(event.start) == datetime.date]

事件具有自然顺序 —— 知道哪个事件最先发生可能有助于分析:

all_events.sort(key=lambda ev: ev.start)

现在,事件已排序,可以将它们加载到每天:

import collections
events_by_day = collections.defaultdict(list)
for event in all_events:
    events_by_day[event.day].append(event)

有了这些,你就有了作为 Python 对象的带有日期、持续时间和序列的日历事件。

用 Python 报到你的生活

现在是时候编写报告代码了!带有适当的标题、列表、重要内容以粗体显示等等,有醒目的格式是很意义。

这就是一些 HTML 和 HTML 模板。我喜欢使用 Chameleon

template_content = """

Day

  • Thing
"""

Chameleon 的一个很酷的功能是使用它的 html 方法渲染对象。我将以两种方式使用它:

  • 摘要将以粗体显示
  • 对于大多数活动,我都会删除摘要(因为这是我的个人信息)
def __html__(self):
    offset = my_timezone.utcoffset(self.start)
    fixed = self.start + offset
    start_str = str(fixed).split("+")[0]
    summary = self.summary
    if summary != "Busy":
        summary = "<REDACTED>"
    return f"{summary[:30]} -- {start_str} ({self.duration})"
Event.__html__ = __html__

为了简洁起见,将该报告切成每天的:

import chameleon
from IPython.display import HTML
template = chameleon.PageTemplate(template_content)
html = template(items=itertools.islice(events_by_day.items(), 3, 4))
HTML(html)

渲染后,它将看起来像这样:

2020-08-25

  • – 2020-08-25 08:30:00 (0:45:00)
  • – 2020-08-25 10:00:00 (1:00:00)
  • – 2020-08-25 11:30:00 (0:30:00)
  • – 2020-08-25 13:00:00 (0:25:00)
  • Busy – 2020-08-25 15:00:00 (1:00:00)
  • – 2020-08-25 15:00:00 (1:00:00)
  • – 2020-08-25 19:00:00 (1:00:00)
  • – 2020-08-25 19:00:12 (1:00:00)

Python 和 Jupyter 的无穷选择

通过解析、分析和报告各种 Web 服务所拥有的数据,这只是你可以做的事情的表面。

为什么不对你最喜欢的服务试试呢?


via: https://opensource.com/article/20/9/calendar-jupyter

作者:Moshe Zadka 选题:lujun9972 译者:stevenzdg988 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

AI+Python数据分析...
教学大纲 01 课程性质和目的 数据分析与可视化为数据科学与大数据...
2026-05-06 20:23:25
飞书 CLI 开源:AI ...
钛媒体App 3月28日消息,飞书官方在 GitHub 开源了命令...
2026-03-28 19:53:17
AI海报丨新春辟谣日历:滑...
⇧点蓝色字关注“互联网联合辟谣平台” 大年初三 滑雪、玩冰 滑雪...
2026-02-19 11:21:26
AI海报丨新春辟谣日历:饺...
⇧点蓝色字关注“互联网联合辟谣平台” 大年初一 福启新岁 饺香迎...
2026-02-17 15:50:19
AI海报丨新春辟谣日历:春...
⇧点蓝色字关注“互联网联合辟谣平台” 今天是除夕 灯火万家 喜气...
2026-02-16 18:48:53
AI海报|新春倒计时日历:...
旧时,人们在腊月二十八这天发好面,准备初一到初五的主食,如今仍有一...
2026-02-15 13:49:25

热门资讯

“Linux 中国” 开源社区... 这是一个艰难的决定 —— 这不是调侃,而是当你真正做出改变了你十几年生活轨迹的决定时,你就会明白这个...
硬核观察 #1263 德国铁路... #1 德国铁路公司招聘 Windows for Workgroups 3.11 管理员该职位要求了解...
人工智能教程(七):Sciki... 在本系列的 上一篇文章 中,我们用 TensorFlow 构建了第一个神经网络,然后还通过 Kera...
关闭 WSL 中正在运行的 L... 以下是使用 WSL 关闭在 Windows 内运行的 Linux 系统的不同方法。你使用 WSL 在...
MX Linux 23.2 “... 最新的 MX Linux 23.2 小版本更新带来了基本的改进和新增功能。MX Linux 推出了 ...
硬核观察 #1262 网络上最... #1 网络上最古老的软件存档库之一即将关闭美国新墨西哥州立大学(NMSU)最近宣布,即将于 2024...
如何在 RHEL 上通过绑定配... 在 Linux 中,你可以根据自己的需要创建高级网络设置,如 网络绑定 Network Bondin...
Linux Matrix 消息... Matrix 用户可以升级 Fractal 6 了!Fractal 是目前 最好的 Matrix 客...
如何通过 VLC 使用字幕 使用 VLC 媒体播放器播放和管理字幕的新手指南。我是一个超级动漫迷,如果你想观看最新的剧集,那么你...
硬核观察 #1261 X 计划... #1 X 计划在奥斯汀建立内容审核总部据报道,该部门将招聘 100 名全职员工,将主要关注 CSE ...