1

我有一个月的日历项目字典(日期作为“键”,列表形式的项目作为“值”),我想以某种方式打印出来(该字典包含在代码中,分配给dct)。我只想显示当前日期或之后的项目今天)。显示格式为:

day: item1, item2

我还希望这些项目仅跨越 5 行标准输出,每行 49 个字符宽(包括空格)。这是必要的,因为输出将显示在 conky(适用于 linux 的应用程序)中。

由于一天可以有多个议程项目,因此必须将输出包装并打印在多行上。我希望代码通过仅选择那些项目可以放入 5 行或更少行的日期来解决这一问题,而不是打印 5 天且相关项目超过 5 行。例如

day1: item1, item2
      item3
day2: item1
day3: item1,
      item2

那是当天/之后的 3 天,打印 5 行,每行 49 个字符宽。超过 49 个字符的字符串用换行符换行。

这是我为此编写的代码:

#!/usr/bin/env python

from datetime import date, timedelta, datetime
import heapq
import re
import textwrap

pattern_string = '(1[012]|[1-9]):[0-5][0-9](\\s)?(?i)(am|pm)'
pattern = re.compile(pattern_string)

# Explanation of pattern_string:
# ------------------------------
#(              #start of group #1
 #1[012]                #  start with 10, 11, 12
 #|             #  or
 #[1-9]             #  start with 1,2,...9
#)              #end of group #1
 #:             #   follow by a semi colon (:)
  #[0-5][0-9]           #     follw by 0..5 and 0..9, which means 00 to 59
            #(\\s)?     #       follow by a white space (optional)
                  #(?i)     #         next checking is case insensitive
                      #(am|pm)  #           follow by am or pm
# The 12-hour clock format is start from 0-12, then a semi colon (:) and follow by 00-59 , and end with am or pm.
# Time format that match:
# 1. "1:00am", "1:00 am","1:00 AM" ,
# 2. "1:00pm", "1:00 pm", "1:00 PM",
# 3. "12:50 pm"

d = date.today() # datetime.date(2013, 8, 11)
e = datetime.today() # datetime.datetime(2013, 8, 11, 5, 56, 28, 702926)
today = d.strftime('%a %b %d') # 'Sun Aug 11'

dct = {
'Thu Aug 01' : [' Weigh In'], 
'Thu Aug 08' : [' 8:00am', 'Serum uric acid test', '12:00pm', 'Make Cheesecake'], 
'Sun Aug 11' : [" Awais chotu's birthday", ' Car wash'], 
'Mon Aug 12' : ['10:00am', 'Start car for 10 minutes'], 
'Thu Aug 15' : [" Hooray! You're Facebook Free!", '10:00am', 'Start car for 10 minutes'], 
'Mon Aug 19' : ['10:00am', 'Start car for 10 minutes'], 
'Thu Aug 22' : ['10:00am', 'Start car for 10 minutes'], 
'Mon Aug 26' : ['10:00am', 'Start car for 10 minutes'], 
'Thu Aug 29' : ['10:00am', 'Start car for 10 minutes']
}

def join_time(lst):
    '''Searches for a time format string in supplied list and concatenates it + the event next to it as an single item
       to a list and returns that list'''
    mod_lst = []
    for number, item in enumerate(lst):
        if re.search(pattern, item):
            mod_lst.append(item + ' ' + lst[number+1]) # append the item (i.e time e.g '1:00am') and the item next to it (i.e. event)
            del lst[number+1]
        else:
            mod_lst.append(item)
    return mod_lst

def parse_date(datestring):
    return datetime.strptime(datestring + ' ' + str(date.today().year), "%a %b %d %Y") # returns a datetime obj for the time string; "Sun Aug 11" = datetime.datetime(1900, 8, 11, 0, 0)

deltas = [] # holds datetime.timedelta() objs; timedelta(days, seconds, microseconds)
val_len = []
key_len = {}

for key in dct:
    num = len(''.join(item for item in dct[key]))
    val_len.append(num) # calculate the combined len of all items in the 
                        # list which are the val of a key and add them to val_len
    if num > 37:
        key_len[key] = 2
    else:
        key_len[key] = 1

# val_len = [31, 9, 61, 31, 31, 49, 31, 32, 31]
# key_len = {'Sun Aug 11': 1, 'Mon Aug 12': 1, 'Thu Aug 01': 1, 'Thu Aug 15': 2, 'Thu Aug 22': 1, 'Mon Aug 19': 1, 'Thu Aug 08': 2, 'Mon Aug 26': 1, 'Thu Aug 29': 1}

counter = 0
for eachLen in val_len:
    if eachLen > 37:
        counter = counter + 2
    else:
        counter = counter + 1

# counter = 11

if counter > 5: # because we want only those 5 events in our conky output which are closest to today
    n = counter - 5 # n = 6, these no of event lines should be skipped

    for key in dct:
        deltas.append(e - parse_date(key)) # today - key date (e.g. 'Sun Aug 11') ---> datetime.datetime(2013, 8, 11, 5, 56, 28, 702926) - datetime.datetime(1900, 8, 11, 0, 0)

    # TODO: 'n' no of event lines should be skipped, NOT n no of days!  
    for key in sorted(dct, key=parse_date): # sorted() returns ['Thu Aug 01', 'Thu Aug 08', 'Sun Aug 11', 'Mon Aug 12', 'Thu Aug 15', 'Mon Aug 19', 'Thu Aug 22', 'Mon Aug 26', 'Thu Aug 29']
        tdelta = e - parse_date(key)
        if tdelta in heapq.nlargest(n, deltas): # heapq.nlargest(x, iterable[, key]); returns list of 'x' no. of largest items in iterable
            pass                                # In this case it should return a list of top 6 largest timedeltas; if the tdelta is in 
                                                # that list, it means its not amongst the 5 events we want to print
        else:
            if key == today:
                value = dct[key]
                val1 = '${color green}' + key + '$color: ' 
                mod_val = join_time(value) 
                val2 = textwrap.wrap(', '.join(item for item in mod_val), 37)
                print val1 + '${color 40E0D0}' + '$color\n          ${color 40E0D0}'.join(item for item in val2) + '$color'
            else:
                value = dct[key]
                mod_val = join_time(value)
                output = key + ': ' + ', '.join(item for item in mod_val)
                print '\n           '.join(textwrap.wrap(output, 49))

else:   
    for key in sorted(dct, key=parse_date):
        if key == today:
            value = dct[key]
            val1 = '${color green}' + key + '$color: ' 
            mod_val = join_time(value) 
            val2 = textwrap.wrap(', '.join(item for item in mod_val), 37)
            print val1 + '${color 40E0D0}' + '$color\n          ${color 40E0D0}'.join(item for item in val2) + '$color'
        else:
            value = dct[key]
            mod_val = join_time(value)
            output = key + ': ' + ', '.join(item for item in mod_val)
            print '\n           '.join(textwrap.wrap(output, 49))

结果是:

Thu Aug 22: 10:00am Start car for 10 minutes
Mon Aug 26: 10:00am Start car for 10 minutes
Thu Aug 29: 10:00am Start car for 10 minutes

我已经对代码进行了大量注释,因此不难弄清楚它是如何工作的。我基本上是使用 datetime 计算距当天最远的日子,并跳过那些日子及其项目。该代码通常运行良好,但有时却不行。在这种情况下,输出应该是:

Mon Aug 19: 10:00am Start car for 10 minutes
Thu Aug 22: 10:00am Start car for 10 minutes
Mon Aug 26: 10:00am Start car for 10 minutes
Thu Aug 29: 10:00am Start car for 10 minutes

因为这些是当天(8 月 16 日星期五)之后的日子,其项目适合 5 行。如何修复它以跳过n离今天最远的天数而不是行数?

我正在考虑使用key_lendict 以某种方式进一步过滤输出,方法是仅打印项目长度总和为 < 或 = 5的那些日子的项目...

我被困住了。

4

1 回答 1

1

很难说出你在这里问什么,而且你的代码是一个巨大的混乱。

但是,您在给定示例中得到错误输出的原因非常明显,并且与TODO代码中的注释相匹配,所以我假设这是您要询问的唯一部分:

 # TODO: 'n' no of event lines should be skipped, NOT n no of days!

我不明白您为什么要跳到今天之后的最后5 行而不是前 5 行,但我认为您有充分的理由。

解决此问题的最简单方法是反向执行,将行添加到字符串而不是print直接将它们插入,当达到 5 行时停止,然后打印字符串。(这也将节省一遍又一遍地重新构建堆的浪费等)

例如,像这样:

outlines = []
for key in sorted(dct, key=parse_date, reverse=True): # sorted() returns ['Thu Aug 01', 'Thu Aug 08', 'Sun Aug 11', 'Mon Aug 12', 'Thu Aug 15', 'Mon Aug 19', 'Thu Aug 22', 'Mon Aug 26', 'Thu Aug 29']
    if parse_date(key) < parse_date(today):
        break
    tdelta = e - parse_date(key)
    if key == today:
        value = dct[key]
        val1 = '${color green}' + key + '$color: ' 
        mod_val = join_time(value) 
        val2 = textwrap.wrap(', '.join(item for item in mod_val), 37)
        outstr = val1 + '${color 40E0D0}' + '$color\n          ${color 40E0D0}'.join(item for item in val2) + '$color'
        outlines[:0] = outstr.splitlines()
    else:
        value = dct[key]
        mod_val = join_time(value)
        output = key + ': ' + ', '.join(item for item in mod_val)
        outstr = '\n           '.join(textwrap.wrap(output, 49))
        outlines[:0] = outstr.splitlines()
    if len(outlines) >= 5:
        break
print '\n'.join(outlines)

有很多方法可以简化这一点。例如,不要传递日期的字符串表示形式并到处使用parse_date,只需传递日期,并在最后格式化一次。使用字符串格式而不是 120 个字符的多重连接表达式。构建一次数据结构并使用它们,而不是在需要它们的地方一遍又一遍地重建它们。等等。但这应该是让它工作所需的一切。

于 2013-08-17T01:45:01.617 回答