def prep(self, private): # print 'prep', self.id, private cached = dict() def _node_data(_node): d = cached.get(_node) iscached = d is not None if not iscached: _upnodes = utils.tree_ups(_node) _itemcats = utils.tree_all_downs(utils.list_flatten(_upnodes, lambda enode: enode.itemcats.all())) d = dict( upnodes = _upnodes, itemcats = _itemcats, items = ItemCat.els_get(_itemcats), ) cached[_node] = d # print '_node_data cached', iscached, _node, d return d loc = self.loc user = loc.user formtypes, forms_ids, repdict_items_ids, repdict_usercats_ids = Form.get_forms_reps( baseuser = user, private = private, visit = self, usercats = user.cats.all(), loccats = loc.cats.all(), **_node_data(self.node) ) addr = loc.addr() def _dt(datetime): return str(datetime) if datetime else '' dt = self.datetime v = dict( name = self.name, datetime = _dt(dt), end = _dt(utils.datetime_plus(dt, self.duration)), status = self.status, accompanied = self.accompanied, f_contact = self.f_contact, f_goal = self.f_goal, f_option = self.f_option, observations = self.observations, user_name = user.fullname(), user_email = user.email, user_cats = utils.db_names(user.cats), loc_name = loc.name, loc_address = '%s # %s, %s' % (addr.street, addr.unit, addr.area), formtypes = formtypes, forms = forms_ids, repdict_items = repdict_items_ids, repdict_usercats = repdict_usercats_ids, rec = self.rec_dict(), ) # print 'prep', self, v return v
def _generate(self): sys = Sys.objects.first() print '_generate', self, sys qn = [] # collection of multiple conds. def _qn_and_or(_qn, _isand): return _qn_and(_qn) if _isand else _qn_or(_qn) for cond in self.conds.all(): eqn = cond.qn() if eqn: qn.append(_qn_and_or(eqn, not self.isand)) if not qn: # not any ([ getattr(self, e).exists() for e in 'usercats loccats areas cities states countries zips bricks'.split() ]) # raise ValidationError('Must select at least one condition for Users / Locs.') self.generate = False self.save() return # revert generate and ABORT, so we do NOT waste this builder. if qn: locs = Loc.objects.filter(_qn_and_or(qn, self.isand)) # print 'query', locs.query locs = list(locs) else: locs = [] def _sortkey(eloc): addr = eloc.addr() by = self.orderby def _val(): v = addr.area if by == 'area': return v if by in [ 'zip', 'brick' ]: v = v.zip return v if by == 'zip' else v.brick v = v.city if by == 'city': return v v = v.state if by == 'state': return v v = v.country if by == 'country': return v error val = _val().name # print '_sortkey', by, val, eloc return val locs = sorted(locs, key=_sortkey) print 'locs', len(locs), locs pcats = utils.tree_all_downs(self.periodcats.all()) pn1 = list(PeriodCat.els_get(pcats)) pn2 = list(self.periods.all()) pn = sorted(set(pn1 + pn2), key=lambda e: e.end) print 'pn', pn def _day(week, date): return getattr(week, utils.weekdays[date.weekday()]) def _onoff(dt, week, sources, suffix): day = _day(week, dt) # print '_onoff', dt, week, day ok = False if day: dtdate = dt.date() dttime = dt.time() intime = False for time in day.times.all(): if dttime >= time.start and dttime <= time.end: intime = True break if intime: sources = utils.list_compact(sources) # remove None's. def _get(prefix, cmp2, checkdate): xn = utils.list_flatten(sources, lambda e: getattr(e, prefix + suffix).all()) # print '_onoff._get', cmp2, xn def _filter(e): return cmp2 >= e.start and cmp2 <= e.end and (not e.date or e.date == dtdate if checkdate else True) return utils.list_last(sorted( [ e for e in xn if _filter(e) ], key = lambda e: e.start, )) def _ison(pt): return not pt or pt.on operiod = _get('onoffperiod', dtdate, False) if _ison(operiod): otime = _get('onofftime', dttime, True) if _ison(otime): ok = True # if not ok: print '_onoff - FAILED', dt return ok with transaction.atomic(): mgr = ForceVisit.objects self.qty_slots = 0 self.qty_slots_skips = 0 self.qty_locs = len(locs) self.qty_locs_skips = 0 self.qty_node_skips = 0 nodeskips = 0 visits = [] for ep in pn: start, end = ep.dates() qv = mgr.filter(datetime__range=(start, end)) # query within the current period (start - end). delta = end - start dates = [ start + datetime.timedelta(days=i) for i in range(delta.days + 1) ] week = ep.week or sys.week_period print '_generate > ep', ep, week, start, end # dates for edate in dates: day = _day(week, edate) print '_generate > edate', edate, day if day: def _datetime(_time): # can't use timedelta with simple times. return datetime.datetime.combine(edate, _time) for etime in day.times.all(): dt = _datetime(etime.start) dt2 = _datetime(etime.end) while dt < dt2: self.qty_slots += 1 # print '_generate > slot', dt user = self.node.user ison = _onoff( dt, (user.week_visit if user else None) or sys.week_user_visit, [ user ], '_visit', ) if ison: if qv.filter(node=self.node, datetime=dt).exists(): # visit already generated for the node in this time slot. self.qty_node_skips += 1 print '_generate > qty_node_skips', dt, self.node else: # try (potentially multiple) locs for this specific time slot. tryloc = True locs2 = [] # queue for tried locs, which should be retried in the next time slot. while locs and tryloc: loc = locs.pop(0) if locs else None # print '_tryloc', dt, loc if loc: if qv.filter(loc=loc): # loc already visited during this period. self.qty_locs_skips += 1 print '_generate > qty_locs_skips', dt, loc else: ison = _onoff( dt, loc.week or loc.user.week_visited or sys.week_user_visited, [ loc, loc.user ], '_visited', ) if ison: visit = mgr.create( builder = self, node = self.node, loc = loc, datetime = dt, duration = self.duration, ) print '==> _generate > visit', dt, visit visits.append(visit) tryloc = False else: # print '_generate > SKIP VISITED (locs2 queue)', dt, loc locs2.append(loc) locs[0:0] = locs2 # re-insert in order at the beginning, for immediate try during the subsequent time slots. else: # print '_generate > SKIP VISIT', dt, user pass dt = utils.datetime_plus(dt, self.duration, self.gap) self.qty_visits = len(visits) print 'done > generated visits & remaining locs', self.qty_visits, len(locs) self.save()