mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-26 21:30:55 +08:00
commit
c667332b21
315
published/20200915 Improve your time management with Jupyter.md
Normal file
315
published/20200915 Improve your time management with Jupyter.md
Normal file
@ -0,0 +1,315 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (stevenzdg988)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-13212-1.html)
|
||||
[#]: subject: (Improve your time management with Jupyter)
|
||||
[#]: via: (https://opensource.com/article/20/9/calendar-jupyter)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
使用 Jupyter 改善你的时间管理
|
||||
======
|
||||
|
||||
> 在 Jupyter 里使用 Python 来分析日历,以了解你是如何使用时间的。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202103/18/095530cxx6663ptypyzvmx.jpg)
|
||||
|
||||
[Python][2] 在探索数据方面具有令人难以置信的可扩展性。利用 [Pandas][3] 或 [Dask][4],你可以将 [Jupyter][5] 扩展到大数据领域。但是小数据、个人资料、私人数据呢?
|
||||
|
||||
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][6] 库。我找到了我的电子邮件服务的 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][7] 解围:
|
||||
|
||||
```
|
||||
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': [<DTEND{}2020-08-25 23:00:00+00:00>],
|
||||
'dtstamp': [<DTSTAMP{}2020-08-25 18:19:15+00:00>],
|
||||
'dtstart': [<DTSTART{}2020-08-25 22:00:00+00:00>],
|
||||
'summary': [<SUMMARY{}Busy>],
|
||||
'uid': [<UID{}1302728i-040000008200E00074C5B7101A82E00800000000D939773EA578D601000000000000000010000000CD71CC3393651B419E9458134FE840F5>]}
|
||||
```
|
||||
|
||||
好吧,至少好一点了。
|
||||
|
||||
仍有一些工作要做,将其转换为合理的 Python 对象。第一步是 _拥有_ 一个合理的 Python 对象。[attrs][8] 库提供了一个不错的开始:
|
||||
|
||||
```
|
||||
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][9]:
|
||||
|
||||
```
|
||||
template_content = """
|
||||
<html><body>
|
||||
<div tal:repeat="item items">
|
||||
<h2 tal:content="item[0]">Day</h2>
|
||||
<ul>
|
||||
<li tal:repeat="event item[1]"><span tal:replace="event">Thing</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body></html>"""
|
||||
```
|
||||
|
||||
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"<b>{summary[:30]}</b> -- {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**
|
||||
|
||||
- **\<REDACTED>** -- 2020-08-25 08:30:00 (0:45:00)
|
||||
- **\<REDACTED>** -- 2020-08-25 10:00:00 (1:00:00)
|
||||
- **\<REDACTED>** -- 2020-08-25 11:30:00 (0:30:00)
|
||||
- **\<REDACTED>** -- 2020-08-25 13:00:00 (0:25:00)
|
||||
- Busy -- 2020-08-25 15:00:00 (1:00:00)
|
||||
- **\<REDACTED>** -- 2020-08-25 15:00:00 (1:00:00)
|
||||
- **\<REDACTED>** -- 2020-08-25 19:00:00 (1:00:00)
|
||||
- **\<REDACTED>** -- 2020-08-25 19:00:12 (1:00:00)
|
||||
|
||||
### Python 和 Jupyter 的无穷选择
|
||||
|
||||
通过解析、分析和报告各种 Web 服务所拥有的数据,这只是你可以做的事情的表面。
|
||||
|
||||
为什么不对你最喜欢的服务试试呢?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/9/calendar-jupyter
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[stevenzdg988](https://github.com/stevenzdg988)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/calendar.jpg?itok=jEKbhvDT (Calendar close up snapshot)
|
||||
[2]: https://opensource.com/resources/python
|
||||
[3]: https://pandas.pydata.org/
|
||||
[4]: https://dask.org/
|
||||
[5]: https://jupyter.org/
|
||||
[6]: https://pypi.org/project/caldav/
|
||||
[7]: https://pypi.org/project/vobject/
|
||||
[8]: https://opensource.com/article/19/5/python-attrs
|
||||
[9]: https://chameleon.readthedocs.io/en/latest/
|
@ -88,7 +88,7 @@ Program received signal SIGSEGV, Segmentation fault.
|
||||
要充分利用 GDB,你需要将调试符号编译到你的可执行文件中。你可以用 GCC 中的 `-g` 选项来生成这个符号:
|
||||
|
||||
```
|
||||
$ g++ -o debuggy example.cpp
|
||||
$ g++ -g -o debuggy example.cpp
|
||||
$ ./debuggy
|
||||
Hello world.
|
||||
Segmentation fault
|
||||
@ -250,7 +250,7 @@ $4 = 02
|
||||
要查看其在内存中的地址:
|
||||
|
||||
```
|
||||
(gdb) print /o beta
|
||||
(gdb) print /o &beta
|
||||
$5 = 0x2
|
||||
```
|
||||
|
||||
|
@ -4,12 +4,14 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-13213-1.html)
|
||||
|
||||
Cinnamon vs MATE vs Xfce:你应该选择那一个 Linux Mint 口味?
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202103/18/111916ljidnfwwsxec1fqf.jpg)
|
||||
|
||||
Linux Mint 无疑是 [最适合初学者的 Linux 发行版之一][1]。尤其是对于刚刚迈向 Linux 世界的 Windows 用户来说,更是如此。
|
||||
|
||||
2006 年以来(也就是 Linux Mint 首次发布的那一年),他们开发了一系列的提高用户的体验的 [工具][2]。此外,Linux Mint 是基于 Ubuntu 的,所以你有一个可以寻求帮助的庞大的用户社区。
|
@ -2,7 +2,7 @@
|
||||
[#]: via: (https://opensource.com/article/21/2/osi-licenses-cal-cern-ohl)
|
||||
[#]: author: (Pam Chestek https://opensource.com/users/pchestek)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: translator: (wyxplus)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
@ -0,0 +1,194 @@
|
||||
[#]: subject: (My favorite open source project management tools)
|
||||
[#]: via: (https://opensource.com/article/21/3/open-source-project-management)
|
||||
[#]: author: (Frank Bergmann https://opensource.com/users/fraber)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
My favorite open source project management tools
|
||||
======
|
||||
If you're managing large and complex projects, try replacing Microsoft
|
||||
Project with an open source option.
|
||||
![Kanban-style organization action][1]
|
||||
|
||||
Projects like building a satellite, developing a robot, or launching a new product are all expensive, involve different providers, and contain hard dependencies that must be tracked.
|
||||
|
||||
The approach to project management in the world of large projects is quite simple (in theory at least). You create a project plan and split it into smaller pieces until you can reasonably assign costs, duration, resources, and dependencies to the various activities. Once the project plan is approved by the people in charge of the money, you use it to track the project's execution. Drawing all of the project's activities on a timeline produces a bar chart called a [Gantt chart][2].
|
||||
|
||||
Gantt charts have always been used in [waterfall project methodologies][3], but they can also be used with agile. For example, large projects may use a Gantt chart for a scrum sprint and ignore other details like user stories, thereby embedding agile phases. Other large projects may include multiple product releases (e.g., minimum viable product [MVP], second version, third version, etc.). In this case, the super-structure is kind of agile, with each phase planned as a Gantt chart to deal with budgets and complex dependencies.
|
||||
|
||||
### Project management tools
|
||||
|
||||
There are literally hundreds of tools available to manage large projects with Gantt charts, and Microsoft Project is probably the most popular. It is part of the Microsoft Office family, scales to hundreds of thousands of activities, and has an incredible number of features that support almost every conceivable way to manage a project schedule. With Project, it's not always clear what is more expensive: the software license or the training courses that teach you how to use the tool.
|
||||
|
||||
Another drawback is that Microsoft Project is a standalone desktop application, and only one person can update a schedule. You would need to buy licenses for Microsoft Project Server, Project for the web, or Microsoft Planner if you want multiple users to collaborate.
|
||||
|
||||
Fortunately, there are open source alternatives to the proprietary tools, including the applications in this article. All are open source and include a Gantt for scheduling hierarchical activities based on resources and dependencies. ProjectLibre, GanttProject, and TaskJuggler are desktop applications for a single project manager; ProjeQtOr and Redmine are web applications for project teams, and ]project-open[ is a web application for managing entire organizations.
|
||||
|
||||
I evaluated the tools based on a single user planning and tracking a single large project. My evaluation criteria includes Gantt editor features, availability on Windows, Linux, and macOS, scalability, import/export, and reporting. (Full disclosure: I'm the founder of ]project-open[, and I've been active in several open source communities for many years. This list includes our product, so my views may be biased, but I tried to focus on each product's best features.)
|
||||
|
||||
### Redmine 4.1.0
|
||||
|
||||
![Redmine][4]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
[Redmine][6] is a web-based project management tool with a focus on agile methodologies.
|
||||
|
||||
The standard installation includes a Gantt timeline view, but it lacks fundamental features like scheduling, drag-and-drop, indent and outdent, and resource assignments. You have to edit task properties individually to change the task tree's structure.
|
||||
|
||||
Redmine has Gantt editor plugins, but they are either outdated (e.g., [Plus Gantt][7]) or proprietary (e.g., [ANKO Gantt chart][8]). If you know of other open source Gantt editor plugins, please share them in the comments.
|
||||
|
||||
Redmine is written in Ruby on Rails and available for Windows, Linux, and macOS. The core is available under a GPLv2 license.
|
||||
|
||||
* **Best for:** IT teams working using agile methodologies
|
||||
* **Unique selling proposition:** It's the original "upstream" parent project of OpenProject and EasyRedmine.
|
||||
|
||||
|
||||
|
||||
### ]project-open[ 5.1
|
||||
|
||||
![\]project-open\[][9]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
[]project-open[][10] is a web-based project management system that takes the perspective of an entire organization, similar to an enterprise resource planning (ERP) system. It can also manage project portfolios, budgets, invoicing, sales, human resources, and other functional areas. Specific variants exist for professional services automation (PSA) for running a project company, project management office (PMO) for managing an enterprise's strategic projects, and enterprise project management (EPM) for managing a department's projects.
|
||||
|
||||
The ]po[ Gantt editor includes hierarchical tasks, dependencies, and scheduling based on planned work and assigned resources. It does not support resource calendars and non-human resources. The ]po[ system is quite complex, and the GUI might need a refresh.
|
||||
|
||||
]project-open[ is written in TCL and JavaScript and available for Windows and Linux. The ]po[ core is available under a GPLv2 license with proprietary extensions available for large companies.
|
||||
|
||||
* **Best for:** Medium to large project organizations that need a lot of financial project reporting
|
||||
* **Unique selling proposition:** ]po[ is an integrated system to run an entire project company or department.
|
||||
|
||||
|
||||
|
||||
### ProjectLibre 1.9.3
|
||||
|
||||
![ProjectLibre][11]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
[ProjectLibre][12] is probably the closest you can get to Microsoft Project in the open source world. It is a desktop application that supports all-important project planning features, including resource calendars, baselines, and cost management. It also allows you to import and export schedules using MS-Project's file format.
|
||||
|
||||
ProjectLibre is perfectly suitable for planning and executing small or midsized projects. However, it's missing some advanced features in MS-Project, and its GUI is not the prettiest.
|
||||
|
||||
ProjectLibre is written in Java and available for Windows, Linux, and macOS and licensed under an open source Common Public Attribution (CPAL) license. The ProjectLibre team is currently working on a Web offering called ProjectLibre Cloud under a proprietary license.
|
||||
|
||||
* **Best for:** An individual project manager running small to midsized projects or as a viewer for project members who don't have a full MS-Project license
|
||||
* **Unique selling proposition:** It's the closest you can get to MS-Project with open source.
|
||||
|
||||
|
||||
|
||||
### GanttProject 2.8.11
|
||||
|
||||
![GanttProject][13]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
[GanttProject][14] is similar to ProjectLibre as a desktop Gantt editor but with a more limited feature set. It doesn't support baselines nor non-human resources, and the reporting functionality is more limited.
|
||||
|
||||
GanttProject is a desktop application written in Java and available for Windows, Linux, and macOS under the GPLv3 license.
|
||||
|
||||
* **Best for:** Simple Gantt charts or learning Gantt-based project management techniques.
|
||||
* **Unique selling proposition:** It supports program evaluation and review technique ([PERT][15]) charts and collaboration using WebDAV.
|
||||
|
||||
|
||||
|
||||
### TaskJuggler 3.7.1
|
||||
|
||||
![TaskJuggler][16]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
[TaskJuggler][17] schedules multiple parallel projects in large organizations, focusing on automatically resolving resource assignment conflicts (i.e., resource leveling).
|
||||
|
||||
It is not an interactive Gantt editor but a command-line tool that works similarly to a compiler: It reads a list of tasks from a text file and produces a series of reports with the optimum start and end times for each task depending on the assigned resources, dependencies, priorities, and many other parameters. It supports multiple projects, baselines, resource calendars, shifts, and time zones and has been designed to scale to enterprise scenarios with many projects and resources.
|
||||
|
||||
Writing a TaskJuggler input file with its specific syntax may be beyond the average project manager's capabilities. However, you can use ]project-open[ as a graphical frontend for TaskJuggler to generate input, including absences, task progress, and logged hours. When used this way, TaskJuggler becomes a powerful what-if scenario planner.
|
||||
|
||||
TaskJuggler is written in Ruby and available for Windows, Linux, and macOS under a GPLv2 license.
|
||||
|
||||
* **Best for:** Medium to large departments managed by a true nerd
|
||||
* **Unique selling proposition:** It excels in automatic resource-leveling.
|
||||
|
||||
|
||||
|
||||
### ProjeQtOr 9.0.4
|
||||
|
||||
![ProjeQtOr][18]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
[ProjeQtOr][19] is a web-based project management application that's suitable for IT projects. It supports risks, budgets, deliverables, and financial documents in addition to projects, tickets, and activities to integrate many aspects of project management into a single system.
|
||||
|
||||
ProjeQtOr provides a Gantt editor with a feature set similar to ProjectLibre, including hierarchical tasks, dependencies, and scheduling based on planned work and assigned resources. However, it doesn't support in-place editing of values (e.g., task name, estimated time, etc.); users must change values in an entry form below the Gantt view and save the values.
|
||||
|
||||
ProjeQtOr is written in PHP and available for Windows, Linux, and macOS under the Affero GPL3 license.
|
||||
|
||||
* **Best for:** IT departments tracking a list of projects
|
||||
* **Unique selling proposition:** Lets you store a wealth of information for every project, keeping all information in one place.
|
||||
|
||||
|
||||
|
||||
### Other tools
|
||||
|
||||
The following systems may be valid options for specific use cases but were excluded from the main list for various reasons.
|
||||
|
||||
![LIbrePlan][20]
|
||||
|
||||
(Frank Bergmann, [CC BY-SA 4.0][5])
|
||||
|
||||
* [**LibrePlan**][21] is a web-based project management application focusing on Gantt charts. It would have figured prominently in the list above due to its feature set, but there is no installation available for recent Linux versions (CentOS 7 or 8). The authors say updated instructions will be available soon.
|
||||
* [**dotProject**][22] is a web-based project management system written in PHP and available under the GPLv2.x license. It includes a Gantt timeline report, but it doesn't have options to edit it, and dependencies don't work yet (they're "only partially functional").
|
||||
* [**Leantime**][23] is a web-based project management system with a pretty GUI written in PHP and available under the GPLv2 license. It includes a Gantt timeline for milestones but without dependencies.
|
||||
* [**Orangescrum**][24] is a web-based project-management tool. Gantt charts are available as a paid add-on or with a paid subscription.
|
||||
* [**Talaia/OpenPPM**][25] is a web-based project portfolio management system. However, version 4.6.1 still says "Coming Soon: Interactive Gantt Charts."
|
||||
* [**Odoo**][26] and [**OpenProject**][27] both restrict some important features to the paid enterprise edition.
|
||||
|
||||
|
||||
|
||||
In this review, I aimed to include all open source project management systems that include a Gantt editor with dependency scheduling. If I missed a project or misrepresented something, please let me know in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/open-source-project-management
|
||||
|
||||
作者:[Frank Bergmann][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/fraber
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/kanban_trello_organize_teams_520.png?itok=ObNjCpxt (Kanban-style organization action)
|
||||
[2]: https://en.wikipedia.org/wiki/Gantt_chart
|
||||
[3]: https://opensource.com/article/20/3/agiles-vs-waterfall
|
||||
[4]: https://opensource.com/sites/default/files/uploads/redmine.png (Redmine)
|
||||
[5]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[6]: https://www.redmine.org/
|
||||
[7]: https://redmine.org/plugins/plus_gantt
|
||||
[8]: https://www.redmine.org/plugins/anko_gantt_chart
|
||||
[9]: https://opensource.com/sites/default/files/uploads/project-open.png (]project-open[)
|
||||
[10]: https://www.project-open.com
|
||||
[11]: https://opensource.com/sites/default/files/uploads/projectlibre.png (ProjectLibre)
|
||||
[12]: http://www.projectlibre.org
|
||||
[13]: https://opensource.com/sites/default/files/uploads/ganttproject.png (GanttProject)
|
||||
[14]: https://www.ganttproject.biz
|
||||
[15]: https://en.wikipedia.org/wiki/Program_evaluation_and_review_technique
|
||||
[16]: https://opensource.com/sites/default/files/uploads/taskjuggler.png (TaskJuggler)
|
||||
[17]: https://taskjuggler.org/
|
||||
[18]: https://opensource.com/sites/default/files/uploads/projeqtor.png (ProjeQtOr)
|
||||
[19]: https://www.projeqtor.org
|
||||
[20]: https://opensource.com/sites/default/files/uploads/libreplan.png (LIbrePlan)
|
||||
[21]: https://www.libreplan.dev/
|
||||
[22]: https://dotproject.net/
|
||||
[23]: https://leantime.io
|
||||
[24]: https://orangescrum.org/
|
||||
[25]: http://en.talaia-openppm.com/
|
||||
[26]: https://odoo.com
|
||||
[27]: http://openproject.org
|
@ -0,0 +1,163 @@
|
||||
[#]: subject: (Programming 101: Input and output with Java)
|
||||
[#]: via: (https://opensource.com/article/21/3/io-java)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
Programming 101: Input and output with Java
|
||||
======
|
||||
Learn how Java handles reading and writing data.
|
||||
![Coffee beans and a cup of coffee][1]
|
||||
|
||||
When you write a program, your application may need to read from and write to files stored on the user's computer. This is common in situations when you want to load or store configuration options, you need to create log files, or your user wants to save work for later. Every language handles this task a little differently. This article demonstrates how to handle data files with Java.
|
||||
|
||||
### Installing Java
|
||||
|
||||
Regardless of your computer's platform, you can install Java from [AdoptOpenJDK][2]. This site offers safe and open source builds of Java. On Linux, you may also find AdoptOpenJDK builds in your software repository.
|
||||
|
||||
I recommend using the latest long-term support (LTS) release. The latest non-LTS release is best for developers looking to try the latest Java features, but it likely outpaces what most users have installed—either by default on their system or installed previously for some other Java application. Using the LTS release ensures you're up-to-date with what most users have installed.
|
||||
|
||||
Once you have Java installed, open your favorite text editor and get ready to code. You might also want to investigate an [integrated development environment for Java][3]. BlueJ is ideal for new programmers, while Eclipse and Netbeans are nice for intermediate and experienced coders.
|
||||
|
||||
### Reading a file with Java
|
||||
|
||||
Java uses the `File` library to load files.
|
||||
|
||||
This example creates a class called `Ingest` to read data from a file. When you open a file in Java, you create a `Scanner` object, which scans the file you provide, line by line. In fact, a `Scanner` is the same concept as a cursor in a text editor, and you can control that "cursor" for reading and writing with `Scanner` methods like `nextLine`:
|
||||
|
||||
|
||||
```
|
||||
import java.io.File;
|
||||
import java.util.Scanner;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
public class Ingest {
|
||||
public static void main([String][4][] args) {
|
||||
|
||||
try {
|
||||
[File][5] myFile = new [File][5]("example.txt");
|
||||
Scanner myScanner = new Scanner(myFile);
|
||||
while (myScanner.hasNextLine()) {
|
||||
[String][4] line = myScanner.nextLine();
|
||||
[System][6].out.println(line);
|
||||
}
|
||||
myScanner.close();
|
||||
} catch ([FileNotFoundException][7] ex) {
|
||||
ex.printStackTrace();
|
||||
} //try
|
||||
} //main
|
||||
} //class
|
||||
```
|
||||
|
||||
This code creates the variable `myfile` under the assumption that a file named `example.txt` exists. If that file does not exist, Java "throws an exception" (this means it found an error in what you attempted to do and says so), which is "caught" by the very specific `FileNotFoundException` library. The fact that there's a library specific to this exact error betrays how common this error is.
|
||||
|
||||
Next, it creates a `Scanner` and loads the file into it. I call it `myScanner` to differentiate it from its generic class template. A `while` loop sends `myScanner` over the file, line by line, for as long as there _is_ a next line. That's what the `hasNextLine` method does: it detects whether there's any data after the "cursor." You can simulate this by opening a file in a text editor: Your cursor starts at the very beginning of the file, and you can use the keyboard to scan through the file with the cursor until you run out of lines.
|
||||
|
||||
The `while` loop creates a variable `line` and assigns it the data of the current line. Then it prints the contents of `line` just to provide feedback. A more useful program would probably parse each line to extract whatever important data it contains.
|
||||
|
||||
At the end of the process, the `myScanner` object closes.
|
||||
|
||||
### Running the code
|
||||
|
||||
Save your code as `Ingest.java` (it's a Java convention to give classes an initial capital letter and name the file to match). If you try to run this simple application, you will probably receive an error because there is no `example.txt` for the application to load yet:
|
||||
|
||||
|
||||
```
|
||||
$ java ./Ingest.java
|
||||
java.io.[FileNotFoundException][7]:
|
||||
example.txt (No such file or directory)
|
||||
```
|
||||
|
||||
What a perfect opportunity to write a Java application that writes data to a file!
|
||||
|
||||
### Writing data to a file with Java
|
||||
|
||||
Whether you're storing data that your user creates with your application or just metadata about what a user did in an application (for instance, game saves or recent songs played), there are lots of good reasons to store data for later use. In Java, this is achieved through the `FileWriter` library, this time by opening a file, writing data into it, and then closing the file:
|
||||
|
||||
|
||||
```
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Exgest {
|
||||
public static void main([String][4][] args) {
|
||||
try {
|
||||
[FileWriter][8] myFileWriter = new [FileWriter][8]("example.txt", true);
|
||||
myFileWriter.write("Hello world\n");
|
||||
myFileWriter.close();
|
||||
} catch ([IOException][9] ex) {
|
||||
[System][6].out.println(ex);
|
||||
} // try
|
||||
} // main
|
||||
}
|
||||
```
|
||||
|
||||
The logic and flow of this class are similar to reading a file. Instead of a `Scanner`, it creates a `FileWriter` object with the name of a file. The `true` flag at the end of the `FileWriter` statement tells `FileWriter` to _append_ text to the end of the file. To overwrite a file's contents, remove the `true`:
|
||||
|
||||
|
||||
```
|
||||
`FileWriter myFileWriter = new FileWriter("example.txt", true);`
|
||||
```
|
||||
|
||||
Because I'm writing plain text into a file, I added my own newline character (`\n`) at the end of the data (`Hello world`) written into the file.
|
||||
|
||||
### Trying the code
|
||||
|
||||
Save this code as `Exgest.java`, following the Java convention of naming the file to match the class name.
|
||||
|
||||
Now that you have the means to create and read data with Java, you can try your new applications, in reverse order:
|
||||
|
||||
|
||||
```
|
||||
$ java ./Exgest.java
|
||||
$ java ./Ingest.java
|
||||
Hello world
|
||||
$
|
||||
```
|
||||
|
||||
Because it appends data to the end, you can repeat your application to write data as many times as you want to add more data to your file:
|
||||
|
||||
|
||||
```
|
||||
$ java ./Exgest.java
|
||||
$ java ./Exgest.java
|
||||
$ java ./Exgest.java
|
||||
$ java ./Ingest.java
|
||||
Hello world
|
||||
Hello world
|
||||
Hello world
|
||||
$
|
||||
```
|
||||
|
||||
### Java and data
|
||||
|
||||
You're don't write raw text into a file very often; in the real world, you probably use an additional library to write a specific format instead. For instance, you might use an XML library to write complex data, an INI or YAML library to write configuration files, or any number of specialized libraries to write binary formats like images or audio.
|
||||
|
||||
For full information, refer to the [OpenJDK documentation][10].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/io-java
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/java-coffee-mug.jpg?itok=Bj6rQo8r (Coffee beans and a cup of coffee)
|
||||
[2]: https://adoptopenjdk.net
|
||||
[3]: https://opensource.com/article/20/7/ide-java
|
||||
[4]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string
|
||||
[5]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+file
|
||||
[6]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system
|
||||
[7]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+filenotfoundexception
|
||||
[8]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+filewriter
|
||||
[9]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+ioexception
|
||||
[10]: https://access.redhat.com/documentation/en-us/openjdk/11/
|
73
sources/tech/20210317 Track aircraft with a Raspberry Pi.md
Normal file
73
sources/tech/20210317 Track aircraft with a Raspberry Pi.md
Normal file
@ -0,0 +1,73 @@
|
||||
[#]: subject: (Track aircraft with a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/21/3/tracking-flights-raspberry-pi)
|
||||
[#]: author: (Patrick Easters https://opensource.com/users/patrickeasters)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
Track aircraft with a Raspberry Pi
|
||||
======
|
||||
Explore the open skies with a Raspberry Pi, an inexpensive radio, and
|
||||
open source software.
|
||||
![Airplane flying with a globe background][1]
|
||||
|
||||
I live near a major airport, and I frequently hear aircraft flying over my house. I also have a curious preschooler, and I find myself answering questions like, "What's that?" and "Where's that plane going?" often. While a quick internet search could answer these questions, I wanted to see if I could answer them myself.
|
||||
|
||||
With a Raspberry Pi, an inexpensive radio, and open source software, I can track aircraft as far as 200 miles from my house. Whether you're answering relentless questions from your kids or are just curious about what's in the sky above you, this is something you can try, too.
|
||||
|
||||
![Flight map][2]
|
||||
|
||||
(Patrick Easters, [CC BY-SA 4.0][3])
|
||||
|
||||
### The protocol behind it all
|
||||
|
||||
[ADS-B][4] is a technology that aircraft use worldwide to broadcast their location. Aircraft use position data gathered from GPS and periodically broadcast it along with speed and other telemetry so that other aircraft and ground stations can track their position.
|
||||
|
||||
Since this protocol is well-known and unencrypted, there are many solutions to receive and parse it, including many that are open source.
|
||||
|
||||
### Gathering the hardware
|
||||
|
||||
Pretty much any [Raspberry Pi][5] will work for this project. I've used an older Pi 1 Model B, but I'd recommend a Pi 3 or newer to ensure you can keep up with the stream of decoded ADS-B messages.
|
||||
|
||||
To receive the ADS-B signals, you need a software-defined radio. Thanks to ultra-cheap radio chips designed for TV tuners, there are quite a few cheap USB receivers to choose from. I use [FlightAware's ProStick Plus][6] because it has a built-in filter to weaken signals outside the 1090MHz band used for ADS-B. Filtering is important since strong signals, such as broadcast FM radio and television, can desensitize the receiver. Any receiver based on RTL-SDR should work.
|
||||
|
||||
You will also need an antenna for the receiver. The options are limitless here, ranging from the [more adventurous DIY options][7] to purchasing a [ready-made 1090MHz antenna][8]. Whichever route you choose, antenna placement matters most. ADS-B reception is line-of-sight, so you'll want your antenna to be as high as possible to extend your range. I have mine in my attic, but I got decent results from my house's upper floor.
|
||||
|
||||
### Visualizing your data with software
|
||||
|
||||
Now that your Pi is equipped to receive ADS-B signals, the real magic happens in the software. Two of the most commonly used open source software projects for ADS-B are [readsb][9] for decoding ADS-B messages and [tar1090][10] for visualization. Combining both provides an interactive map showing all the aircraft your Pi is tracking.
|
||||
|
||||
Both projects provide setup instructions, but using a prebuilt image like the [ADSBx Custom Pi Image][11] is the fastest way to get going. The ADSBx image even configures a Prometheus instance with custom metrics like aircraft count.
|
||||
|
||||
### Keep experimenting
|
||||
|
||||
If the novelty of tracking airplanes with your Raspberry Pi wears off, there are plenty of ways to keep experimenting. Try different antenna designs or find the best antenna placement to maximize the number of aircraft you see.
|
||||
|
||||
These are just a few of the ways to track aircraft with your Pi, and hopefully, this inspires you to try it out and learn a bit about the world of radio. Happy tracking!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/tracking-flights-raspberry-pi
|
||||
|
||||
作者:[Patrick Easters][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/patrickeasters
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/plane_travel_world_international.png?itok=jG3sYPty (Airplane flying with a globe background)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/flightmap.png (Flight map)
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://en.wikipedia.org/wiki/Automatic_Dependent_Surveillance%E2%80%93Broadcast
|
||||
[5]: https://www.raspberrypi.org/
|
||||
[6]: https://www.amazon.com/FlightAware-FA-PROSTICKPLUS-1-Receiver-Built-Filter/dp/B01M7REJJW
|
||||
[7]: http://www.radioforeveryone.com/p/easy-homemade-ads-b-antennas.html
|
||||
[8]: https://www.amazon.com/s?k=1090+antenna+sma&i=electronics&ref=nb_sb_noss_2
|
||||
[9]: https://github.com/wiedehopf/readsb
|
||||
[10]: https://github.com/wiedehopf/tar1090
|
||||
[11]: https://www.adsbexchange.com/how-to-feed/adsbx-custom-pi-image/
|
@ -1,300 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (stevenzdg988)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Improve your time management with Jupyter)
|
||||
[#]: via: (https://opensource.com/article/20/9/calendar-jupyter)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
使用 Jupyter 改善你的时间管理
|
||||
======
|
||||
通过在 Jupyter 里使用 Python 分析日历了解您如何使用时间 。
|
||||
![日历特写快照][1]
|
||||
|
||||
[Python][2] 在用于探索数据方面具有令人难以置信的可扩展性选项。利用 [Pandas][3] 或 [Dask][4],您可以将 [Jupyter][5] 扩展到大数据(处理)。但是小数据呢?个人资料?私人数据?
|
||||
|
||||
JupyterLab 和 Jupyter Notebook 提供了一个绝佳的环境来仔细检查我基于笔记本电脑的生活。
|
||||
|
||||
我的探索是基于以下事实:我使用的几乎每个服务都有一个 Web 应用程序编程接口(API)。我使用了许多此类服务:待办事项列表,时间跟踪器,习惯跟踪器等。但是几乎每个人都使用:_日历_。相同的想法可以应用于其他服务,但是日历具有一个很酷的功能:几乎所有网络日历都支持的开放标准: `CalDAV`。
|
||||
|
||||
### 在 Jupyter 中使用 Python 解析日历
|
||||
|
||||
大多数日历提供了导出为 `CalDAV` 格式的方法。您可能需要某种身份验证才能访问此私有数据。按照您的服务说明进行操作即可。如何获得凭据取决于您的服务,但是最终,您应该能够将它们存储在文件中。我将我的名为 `.caldav` 的文件存储在根(root)目录中:
|
||||
|
||||
```
|
||||
import os
|
||||
with open(os.path.expanduser("~/.caldav")) as fpin:
|
||||
username, password = fpin.read().split()
|
||||
```
|
||||
|
||||
切勿将用户名和密码直接放在笔记本中!他们可能会很容易因 `git push` 偏离而导致泄漏。
|
||||
|
||||
下一步是使用方便的 PyPI [caldav][6] 库。我在 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)`[/code] [code]
|
||||
|
||||
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][7] 前来解围:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
[/code] [code]`parse_event(raw_events[12])`[/code] [code]
|
||||
|
||||
{'dtend': [<DTEND{}2020-08-25 23:00:00+00:00>],
|
||||
'dtstamp': [<DTSTAMP{}2020-08-25 18:19:15+00:00>],
|
||||
'dtstart': [<DTSTART{}2020-08-25 22:00:00+00:00>],
|
||||
'summary': [<SUMMARY{}Busy>],
|
||||
'uid': [<UID{}1302728i-040000008200E00074C5B7101A82E00800000000D939773EA578D601000000000000000010000000CD71CC3393651B419E9458134FE840F5>]}
|
||||
```
|
||||
|
||||
好吧,至少好一点了。
|
||||
|
||||
仍有一些工作要做,以将其转换为合理的 Python 对象。第一步是 _拥有_ 一个合理的 Python 对象。[attrs][8] 库提供了一个不错的开始:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
[/code] [code]`get_piece(_, "dtstart")`[/code] [code]` 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()`[/code] [code]`from_calendar_event_and_timezone(raw_events[12], my_timezone)`[/code] [code]` 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)
|
||||
|
||||
[/code] [code]`print(_.day)`[/code] [code]` 2020-08-25`
|
||||
```
|
||||
|
||||
事件始终在内部表示为开始/结束,但是知道持续时间是有用的属性。持续时间也可以添加到现有类中:
|
||||
|
||||
```
|
||||
def duration(self):
|
||||
return self.end - self.start
|
||||
Event.duration = property(duration)
|
||||
|
||||
[/code] [code]`print(_.duration)`[/code] [code]` 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(变色龙:动态的)][9]:
|
||||
|
||||
```
|
||||
template_content = """
|
||||
<html><body>
|
||||
<div tal:repeat="item items">
|
||||
<h2 tal:content="item[0]">Day</h2>
|
||||
<ul>
|
||||
<li tal:repeat="event item[1]"><span tal:replace="event">Thing</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body></html>"""
|
||||
```
|
||||
|
||||
Chameleon 的一个很酷的功能是使用 `html` 方法渲染对象。我将以两种方式使用它:
|
||||
|
||||
* 摘要将以 **bold** (**粗体**)显示
|
||||
* 对于大多数活动,我都会删除摘要(因为这是我的个人信息)
|
||||
|
||||
```
|
||||
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 = "&lt;REDACTED&gt;"
|
||||
return f"<b>{summary[:30]}</b> \-- {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][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[stevenzdg988](https://github.com/stevenzdg988)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/calendar.jpg?itok=jEKbhvDT (Calendar close up snapshot)
|
||||
[2]: https://opensource.com/resources/python
|
||||
[3]: https://pandas.pydata.org/
|
||||
[4]: https://dask.org/
|
||||
[5]: https://jupyter.org/
|
||||
[6]: https://pypi.org/project/caldav/
|
||||
[7]: https://pypi.org/project/vobject/
|
||||
[8]: https://opensource.com/article/19/5/python-attrs
|
||||
[9]: https://chameleon.readthedocs.io/en/latest/
|
Loading…
Reference in New Issue
Block a user