1# -*- coding: utf-8 -*- 2from __future__ import absolute_import 3from __future__ import unicode_literals 4 5from datetime import date, timedelta 6from calendar import monthrange 7 8from django.utils import timezone 9from django.utils.six.moves import xrange 10 11 12get_now = lambda: timezone.localtime(timezone.now()) 13 14now = get_now() 15# Warning! please do not use 'now' anywhere: it is initialized on first import 16# and so it will not change for days in some environments. It's here just for backward 17# compatibility 18 19 20def inc_month(month, year): 21 """ 22 Increment the month and, if neccessary, the year. 23 Both month and year should be ints. 24 """ 25 month += 1 26 if month > 12: 27 month = 1 28 year += 1 29 return month, year 30 31 32def _inc_day(year, month, day, net): 33 """Increments the day by converting to a datetime.date().""" 34 d = date(year, month, day) 35 new_d = d + timezone.timedelta(days=net) 36 return new_d.year, new_d.month, new_d.day 37 38 39def get_net_category_tag(req): 40 if req.META['QUERY_STRING']: 41 net = get_net(req) 42 category, tag = get_category_tag(req) 43 else: 44 net = 0 45 category = None 46 tag = None 47 return net, category, tag 48 49 50def get_net(req): 51 """Get the net of any 'next' and 'prev' querystrings.""" 52 try: 53 nxt, prev = map( 54 int, (req.GET.get('cal_next', 0), req.GET.get('cal_prev', 0)) 55 ) 56 net = nxt - prev 57 except Exception: 58 net = 0 59 return net 60 61 62def get_category_tag(req): 63 """Get value of any category and/or tag querystrings""" 64 return req.GET.get('cal_category', None), req.GET.get('cal_tag', None) 65 66 67def get_qs(old_qs): 68 there = ( 69 'cal_mini', 'cal_next', 'cal_prev', 'cal_month', 'cal_year' 70 ) 71 return [x for x in old_qs.split('&') if x.split('=')[0] not in there] 72 73 74# TODO change from d to something more descriptive 75def order_events(events, d=False): 76 """ 77 Group events that occur on the same day, then sort them alphabetically 78 by title, then sort by day. Returns a list of tuples that looks like 79 [(day: [events])], where day is the day of the event(s), and [events] 80 is an alphabetically sorted list of the events for the day. 81 """ 82 ordered_events = {} 83 for event in events: 84 try: 85 for occ in event.occurrence: 86 try: 87 ordered_events[occ].append(event) 88 except Exception: 89 ordered_events[occ] = [event] 90 except AttributeError: # no occurrence for this event 91 # This shouldn't happen, since an event w/o an occurrence 92 # shouldn't get this far, but if it does, just skip it since 93 # it shouldn't be displayed on the calendar anyway. 94 pass 95 96 if d: 97 # return as a dict without sorting by date 98 return ordered_events 99 else: 100 # return ordered_events as a list tuples sorted by date 101 return sorted(ordered_events.items()) 102 103 104def get_next_and_prev(net): 105 """Returns what the next and prev querystrings should be.""" 106 if net == 0: 107 nxt = prev = 1 108 elif net > 0: 109 nxt = net + 1 110 prev = -(net - 1) 111 else: 112 nxt = net + 1 113 prev = abs(net) + 1 114 return nxt, prev 115 116 117def _check_year(year, month, error, error_msg): 118 """Checks that the year is within 50 years from now.""" 119 if year not in xrange((now.year - 50), (now.year + 51)): 120 year = now.year 121 month = now.month 122 error = error_msg 123 return year, month, error 124 125 126def clean_year_month_day(year, month, day, net): 127 error = False 128 error_msg = "The date given was invalid." 129 # check that the month is within 1-12 130 if month not in xrange(1, 13): 131 month = now.month 132 error = error_msg 133 # check that the day is within range for the month 134 if day not in xrange(1, monthrange(year, month)[1] + 1): 135 day = 1 136 error = error_msg 137 # if no error yet, increment the day by net then check the year 138 if not error: 139 year, month, day = _inc_day(year, month, day, net) 140 year, month, error = _check_year(year, month, error, error_msg) 141 return year, month, day, error 142 143 144def clean_year_month(year, month, month_orig): 145 """ 146 If 'month_orig', which is the month given in the url BEFORE any next/prev 147 query strings have been applied, is out of range, sets month to the 148 current month and returns an error message. Also Returns an error 149 message if the year given is +/- 50 years from now. 150 If 'month', which is the month given in the url AFTER any next/prev 151 query strings have been applied, is out of range, adjusts it to be 152 in range (by also adjusting the year). 153 """ 154 error = False 155 error_msg = "The date given was invalid." 156 if month_orig not in xrange(1, 13) and month_orig is not None: 157 month = now.month 158 error = error_msg 159 # This takes care of 'next' query strings making month > 12 160 while month > 12: 161 month -= 12 162 year += 1 163 # This takes care of 'prev' query strings making month < 1 164 while month < 1: 165 month += 12 166 year -= 1 167 year, month, error = _check_year(year, month, error, error_msg) 168 return year, month, error 169 170 171def check_weekday(year, month, day, reverse=False): 172 """ 173 Make sure any event day we send back for weekday repeating 174 events is not a weekend. 175 """ 176 d = date(year, month, day) 177 while d.weekday() in (5, 6): 178 if reverse: 179 d -= timedelta(days=1) 180 else: 181 d += timedelta(days=1) 182 return d.year, d.month, d.day 183