Как создать список итогов по длительности? - PullRequest
2 голосов
/ 03 февраля 2012

Я хочу рассчитать бонус на основе двух месяцев подряд, где продаж больше всего. Таким образом, я могу повторять итоговое значение каждые два месяца подряд, чтобы найти максимальное значение, т.е. получить

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

Так что мне может понадобиться список пар объектов даты и времени или что-то подобное.

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

Таким образом, между началом и концом я должен разделить время на перекрывающиеся пары последовательных 2 месяцев, например. если первый заказ был в декабре 2008 года, а последний заказ был в ноябре 2011 года, то список, из которого можно выбрать максимум, должен составлять [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

Ближе к тому, что я имею в виду, берется максимум значений некоторых функций от начала до настоящего времени. Базовым примером может служить то, что последний день следующего месяца - это будущее, а вспомогательная функция может быть рекурсивной, но у меня не было времени или помощи, чтобы сделать ее рекурсивной, она работает только для первых 2 периодов, т.е. 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 Ответ

1 голос
/ 10 мая 2012

Это звучит как хорошая работа для функционального подхода .В конце приведен полный рабочий пример, но я просто хочу подчеркнуть элегантность и простоту основной функции, написанную в стиле 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)
...