如何创建持续时间的总计列表?

时间:2012-02-03 15:52:46

标签: python time recursion google-cloud-datastore python-2.x

我想根据销售额最多的连续两个月来计算奖金。所以我可以连续两个月迭代一次,找到最大值,即得到

value = Max[total_between_firstdayMonth1_and_lastDayMonth2, total_between_firstdayMonth2_and_lastDayMonth3, ... , total_between_firstdaySecondToLastMonth_andlastDayLastMonth]

所以我可能需要一组datetime对象列表或类似的东西。

start= model.Order.order('created').get().created # get the oldest order
end = model.Order.order('-created').get().created # get the newest order

因此,在开始和结束之间,我必须在连续2个月的重叠对中划分时间,例如。如果第一个订单是在2008年12月,而最后一个订单是在2011年11月,那么从哪里选择最大值的列表应为[total_december2008 + total_january2009, total_january2009 + total_february2009, ... , total_october2011 + total_november2011]

但是,如果我知道如上所述的开始,我如何得到第二个月的最后一天?如何创建时间和总计列表?

我可能无法立即创建总计列表,但如果我可以创建开始和结束列表,那么我可以调用我们可以假设的辅助函数,例如。

total(start_datetime, end_datetime)

感谢您的帮助

更新

我想我找到了如何计算时间轴从下个月的任何日期到最后一天的示例时间间隔:

>>> d = date(2007,12,18)
>>> print d
2007-12-18
>>> d + relativedelta(months=2) - timedelta(days=d.day)
datetime.date(2008, 1, 31)

更新2

我可以计算第一个持续时间的第一级。现在我只需要概括它来遍历所有持续时间并检查哪个是最高级别:

def level(self):
    startdate = model.Order.all().filter('status =', 'PAID').filter('distributor_id =' , self._key.id()).get().created.date()
    last_day_nextmonth =startdate + relativedelta(months=2) - timedelta(days=1)
    if self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) < 25:
        maxlevel = _('New distributor')
    elif self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)   > 25:
        maxlevel = _('Assistant Teamleader')
    return maxlevel

更新3

更接近于我的意思是从开始到现在采用一些函数值的最大值。 Basecase可以是下个月的最后一天是未来,辅助函数可以是递归的,但我没有时间或帮助使它递归到它只适用于前两个时期,即从开始起4个月:

def level(self):
    startdate = model.Order.all().filter('status =', 'PAID'
            ).filter('distributor_id =',
                     self._key.id()).get().created.date()
    last_day_nextmonth = startdate + relativedelta(months=2) \
        - timedelta(days=1)
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
    if total >= 125:
        level = 5
    elif total >= 75:
        level = 4
    elif total >= 25:
        level = 3
    elif total >= 2:
        level = 2
    else:
        level = 1
    return self.levelHelp(level, last_day_nextmonth + timedelta(days=1))

def levelHelp(self, level, startdate):
    #if startdate in future return level
    last_day_nextmonth = startdate + relativedelta(months=2) \
        - timedelta(days=1)
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
    if total >= 125:
        newlevel = 5
    elif total >= 75:
        newlevel = 4
    elif total >= 25:
        newlevel = 3
    elif total >= 2:
        newlevel = 2
    else:
        newlevel = 1
    return level if level > newlevel else newlevel

更新4

我添加了递归,其中基本情况是将来的下一步,如果是,它将返回最大级别:

def level(self):
    startdate = model.Order.all().filter('status =', 'PAID'
            ).filter('distributor_id =',
                     self._key.id()).get().created.date()
    last_day_nextmonth = startdate + relativedelta(months=2) \
        - timedelta(days=1)
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
    if total >= 125:
        level = 5
    elif total >= 75:
        level = 4
    elif total >= 25:
        level = 3
    elif total >= 2:
        level = 2
    else:
        level = 1
    return self.levelHelp(level, last_day_nextmonth + timedelta(days=1))

def levelHelp(self, level, startdate):

    last_day_nextmonth = startdate + relativedelta(months=2) \
        - timedelta(days=1)
    total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
    if total >= 125:
        newlevel = 5
    elif total >= 75:
        newlevel = 4
    elif total >= 25:
        newlevel = 3
    elif total >= 2:
        newlevel = 2
    else:
        newlevel = 1

    maxlevel = level if level > newlevel else newlevel

    nextstart = last_day_nextmonth + timedelta(days=1)
    now = datetime.now().date()
    if nextstart > now: #next start in is the future
        return maxlevel
    else: return self.levelHelp(maxlevel, nextstart)

1 个答案:

答案 0 :(得分:1)

对于功能方法来说,这听起来不错。最后有一个完整的工作示例,但我只想强调核心功能的优雅和简洁,用FP风格编写:

def find_best_two_months(orders):
  first  = lambda x: x[0]
  second = lambda x: x[1]

  orders_by_year_and_month = [ 
    ("%04d-%02d" % (date.year, date.month), amount) 
    for date, amount in orders]

  sorted_orders = sorted(orders_by_year_and_month, key=first)

  totals_by_month = [ 
    (ym, sum(map(second, groupped_orders))) 
    for ym, groupped_orders in groupby(sorted_orders, key=first)]

  totals_two_months = [ 
    ( "%s - %s" % (m1[0], m2[0]), m1[1]+m2[1] ) 
    for m1, m2 in zip(totals_by_month, totals_by_month[1:]) ]

  return max(totals_two_months, key=second)

以下是一个包​​含评论的完整工作示例:

#!/usr/bin/python
from random import randint
from datetime import date, timedelta
from itertools import groupby

""" finding best two months the functional way """

def find_best_two_months(orders):
  """
  Expect a list of tuples of form (date_of_order, amount):

  [ (date1, amount1), (date2, amount2), ...]
  """

  " helper functions for extracting first or second from tuple "
  first  = lambda x: x[0]
  second = lambda x: x[1]

  " converts [(date, amount)] -> [(YYYY-MM, amount)] "
  orders_by_year_and_month = [ ("%04d-%02d" % (date.year, date.month), amount) for date, amount in orders]

  " Sorts by YYYY-MM. This step can be omitted if orders were already sorted by date"
  sorted_orders = sorted(orders_by_year_and_month, key=first)

  " Compresses orders from the same month, so ve get [(YYYY-MM), total_amount_of_orders]"
  totals_by_month = [ (ym, sum(map(lambda x:x[1], groupped_orders))) 
    for ym, groupped_orders in groupby(sorted_orders, key=first)]

  " Zips orders to two month periods"
  totals_two_months = [ ("%s - %s" % (m1[0], m2[0]), m1[1]+m2[1]) for m1, m2 in zip(totals_by_month, totals_by_month[1:]) ]

  " Returns two-month period with maximum total amount. If there were many periods with max amount, only the first is returned "
  return max(totals_two_months, key=second)

""" 
this code is for generating random list of orders 
and is not a part of the solution 
"""
MIN_AMOUNT=70
MAX_AMOUNT=500
MAX_DAY_SPREAD=5

def gen_order(last_date):
  """ returns (order_date, amount) """
  days = timedelta()
  return (
      last_date+timedelta(days=randint(0, MAX_DAY_SPREAD)),  # new date
      randint(MIN_AMOUNT, MAX_AMOUNT)) # amount

def gen_orders(total, start_date):
  orders = []
  last_date = start_date
  for i in range(total):
    order = gen_order(last_date)
    orders.append(order)
    last_date = order[0]
  return orders

if __name__ == "__main__":
  orders = gen_orders(300, date(2010,1,1)) 
  print find_best_two_months(orders)