def save(self, *args, **kwargs): if self.id is None: self.created_at = now() if self.entered and self.exited: self._duration = self.duration self.updated_at = now() super(StateLog, self).save(*args, **kwargs)
def _fixtures_for_test_current_struct(self): from kardboard.util import now from kardboard.util import relativedelta # Speedy cards for i in xrange(0, 5): k = self.make_card( _service_class="Speedy", backlog_date=now() - relativedelta(days=3), start_date=now() - relativedelta(days=1), ) k.save() # Normal cards for i in xrange(0, 10): k = self.make_card(_service_class="Normal", backlog_date=now(), start_date=now() + relativedelta(days=10)) k.save() # No class cards for i in xrange(0, 5): k = self.make_card(backlog_date=now(), start_date=now() + relativedelta(days=10)) k.save() for i in xrange(0, 3): k = self.make_card( _service_class="Normal", backlog_date=now(), start_date=now() + relativedelta(days=8), done_date=now() + relativedelta(days=15), ) k.save()
def duration(self): if self.unblocked_at is not None: unblocked_at = self.unblocked_at else: unblocked_at = now() delta = unblocked_at - self.blocked_at return delta_in_hours(delta)
def kard_pre_save(cls, sender, document, **kwargs): observed_card = document if observed_card.state_changing is False: # No need to worry about logging it, nothing's changing! return None try: observed_card.old_state except AttributeError: print observed_card print type(observed_card) print dir(observed_card) print 'old_state' in dir(observed_card) raise # If you're here it's because the observed_card's state is changing if observed_card.old_state is not None: try: slo = cls.objects.get(card=observed_card, state=observed_card.old_state) slo.exited = now() slo.save() # Close the old state log except cls.DoesNotExist: # For some reason we didn't record the old state, this should only happen when first rolled out pass
def save(self, *args, **kwargs): self.backlog_date = self._convert_dates_to_datetimes(self.backlog_date) self.start_date = self._convert_dates_to_datetimes(self.start_date) self.done_date = self._convert_dates_to_datetimes(self.done_date) if not self.created_at: self.created_at = now() # Auto move to done if self.done_date: states = States() self.in_progress = False self.state = states.done # Auto fill in final cycle and lead time if self.done_date and self.start_date: self._cycle_time = self.cycle_time self._lead_time = self.lead_time # If a card is blocked, inspect it's previous state and # if we're moving states unblock it if self.blocked: try: k = Kard.objects.only("state").get(key=self.key) if k.state != self.state: # Card is blocked and it's state is about to change self.unblock() except Kard.DoesNotExist: # Card isn't saved can't find its previous state pass self._service_class = self.service_class self.key = self.key.upper() super(Kard, self).save(*args, **kwargs)
def queue_service_class_reports(): from kardboard.app import app from kardboard.models import ServiceClassRecord, ServiceClassSnapshot from kardboard.util import now, month_ranges logger = queue_service_class_reports.get_logger() report_groups = app.config.get('REPORT_GROUPS', {}) group_slugs = report_groups.keys() group_slugs.append('all') for slug in group_slugs: logger.info("ServiceClassSnapshot: %s" % slug) ServiceClassSnapshot.calculate(slug) for x in [1, 3, 6, 9, 12]: start = now() months_ranges = month_ranges(start, x) start_date = months_ranges[0][0] end_date = months_ranges[-1][1] try: logger.info("ServiceClassRecord: %s %s - %s" % (slug, start_date, end_date)) ServiceClassRecord.calculate( start_date=start_date, end_date=end_date, group=slug, ) except Exception, e: msg = "ERROR: Couldn't calc record: %s / %s / %s" % \ (slug, start_date, end_date) log_exception(e, msg)
def cards(self): if self._cards: return self._cards in_progress_q = Q(state__in=self.states.in_progress, team__in=self.teams) ordered_backlog_q = Q(state=self.states.backlog, team__in=self.teams, priority__exists=True) unordered_backlog_q = Q(state=self.states.backlog, team__in=self.teams, priority__exists=False) total_backlog_q = Q(state=self.states.backlog, team__in=self.teams) done_q = Q(done_date__gte=now() - relativedelta(days=self.done_days), team__in=self.teams) if self.backlog_limit: ordered_backlog_cards = Kard.objects.filter(ordered_backlog_q).order_by("priority", "created_at") ordered_backlog_cards = ordered_backlog_cards.limit(self.backlog_limit).exclude("_ticket_system_data") unordered_backlog_cards = [] if len(ordered_backlog_cards) < self.backlog_limit: unordered_backlog_cards = Kard.objects.filter(unordered_backlog_q).order_by("created_at") unordered_backlog_cards = unordered_backlog_cards.limit(self.backlog_limit).exclude( "_ticket_system_data" ) backlog_cards = list(ordered_backlog_cards) + list(unordered_backlog_cards) backlog_cards = backlog_cards[: self.backlog_limit] cards_query = in_progress_q | done_q cards = list(Kard.objects.filter(cards_query).exclude("_ticket_system_data")) self._cards = backlog_cards + cards else: cards_query = total_backlog_q | in_progress_q | done_q self._cards = list(Kard.objects.filter(cards_query).exclude("_ticket_system_data")) return self._cards
def kard_pre_save(cls, sender, document, **kwargs): observed_card = document if observed_card.state_changing is False: # No need to worry about logging it, nothing's changing! return None try: observed_card.old_state except AttributeError: raise # If you're here it's because the observed_card's state is changing if observed_card.old_state is not None: try: slos = cls.objects.filter( card=observed_card, state=observed_card.old_state, service_class=observed_card.service_class.get('name'), exited__exists=False) for s in slos: s.exited = now() s.save() # Close the old state log except cls.DoesNotExist: # For some reason we didn't record the old state, this should only happen when first rolled out pass
def for_team_board(self, team, backlog_limit, done_days): states = States() in_progress_q = Q( state__in=states.in_progress, team=team) done_q = Q(done_date__gte=now() - relativedelta(days=done_days), team=team) cards_query = in_progress_q | done_q wip_and_done = list( self.filter(cards_query).exclude('_ticket_system_data') ) ordered_backlog_q = Q( state=states.backlog, team=team, priority__exists=True) unordered_backlog_q = Q( state=states.backlog, team=team, priority__exists=False) ordered_backlog_cards = self.filter(ordered_backlog_q).order_by('priority', 'created_at') ordered_backlog_cards = ordered_backlog_cards.limit(backlog_limit).exclude('_ticket_system_data') unordered_backlog_cards = [] if len(ordered_backlog_cards) < backlog_limit: ordered_backlog_cards = list(ordered_backlog_cards) unordered_backlog_cards = Kard.objects.filter(unordered_backlog_q).order_by('created_at') unordered_backlog_cards = unordered_backlog_cards.limit(backlog_limit).exclude('_ticket_system_data') backlog = list(ordered_backlog_cards) + list(unordered_backlog_cards) backlog = backlog[:backlog_limit] return backlog + wip_and_done
def report_service_class(group="all", months=None): from kardboard.app import app service_class_order = app.config.get("SERVICE_CLASSES", {}).keys() service_class_order.sort() service_classes = [app.config["SERVICE_CLASSES"][k] for k in service_class_order] if months is None: # We want the current report try: scr = ServiceClassSnapshot.objects.get(group=group) except ServiceClassSnapshot.DoesNotExist: scr = ServiceClassSnapshot.calculate(group=group) time_range = "current" else: start = now() months_ranges = month_ranges(start, months) start_date = months_ranges[0][0] end_date = months_ranges[-1][1] try: scr = ServiceClassRecord.objects.get(group=group, start_date=start_date, end_date=end_date) except ServiceClassRecord.DoesNotExist: scr = ServiceClassRecord.calculate(group=group, start_date=start_date, end_date=end_date) time_range = "past %s months" % months context = { "title": "Service classes: %s" % time_range, "service_classes": service_classes, "data": scr.data, "updated_at": scr.updated_at, "version": VERSION, } return render_template("report-service-class.html", **context)
def for_team_board(self, team, backlog_limit, done_days): states = States() in_progress_q = Q(state__in=states.in_progress, team=team) done_q = Q(done_date__gte=now() - relativedelta(days=done_days), team=team) cards_query = in_progress_q | done_q wip_and_done = list( self.filter(cards_query).exclude('_ticket_system_data')) ordered_backlog_q = Q(state=states.backlog, team=team, priority__exists=True) unordered_backlog_q = Q(state=states.backlog, team=team, priority__exists=False) ordered_backlog_cards = self.filter(ordered_backlog_q).order_by( 'priority', 'created_at') ordered_backlog_cards = ordered_backlog_cards.limit( backlog_limit).exclude('_ticket_system_data') unordered_backlog_cards = [] if len(ordered_backlog_cards) < backlog_limit: ordered_backlog_cards = list(ordered_backlog_cards) unordered_backlog_cards = Kard.objects.filter( unordered_backlog_q).order_by('created_at') unordered_backlog_cards = unordered_backlog_cards.limit( backlog_limit).exclude('_ticket_system_data') backlog = list(ordered_backlog_cards) + list(unordered_backlog_cards) backlog = backlog[:backlog_limit] return backlog + wip_and_done
def kard_post_save(cls, sender, document, **kwargs): observed_card = document # This could be a freshly created card, so create a log for it sl, created = cls.objects.get_or_create(auto_save=False, card=observed_card, state=observed_card.state) if created: sl.entered = now() sl.save()
def card_block(key): try: card = Kard.objects.get(key=key) action = 'block' if card.blocked: action = 'unblock' except Kard.DoesNotExist: abort(404) if action == 'block': f = CardBlockForm(request.form, blocked_at=now()) if action == 'unblock': f = CardUnblockForm(request.form, unblocked_at=now()) if 'cancel' in request.form.keys(): return True # redirect elif request.method == "POST" and f.validate(): if action == 'block': blocked_at = datetime.datetime.combine(f.blocked_at.data, datetime.time()) blocked_at = make_start_date(date=blocked_at) result = card.block(f.reason.data, blocked_at) if result: card.save() flash("%s blocked" % card.key) return True # redirect if action == 'unblock': unblocked_at = datetime.datetime.combine(f.unblocked_at.data, datetime.time()) unblocked_at = make_end_date(date=unblocked_at) result = card.unblock(unblocked_at) if result: card.save() flash("%s unblocked" % card.key) return True # redurect context = { 'title': "%s a card" % (action.capitalize(), ), 'action': action, 'card': card, 'form': f, 'updated_at': datetime.datetime.now(), 'version': VERSION, } return render_template('card-block.html', **context)
def _set_dates(self): self.backlog_date = self._convert_dates_to_datetimes(self.backlog_date) self.start_date = self._convert_dates_to_datetimes(self.start_date) self.done_date = self._convert_dates_to_datetimes(self.done_date) self.due_date = self._convert_dates_to_datetimes(self.due_date) if not self.created_at: self.created_at = now()
def card_block(key): try: card = Kard.objects.get(key=key) action = 'block' if card.blocked: action = 'unblock' except Kard.DoesNotExist: abort(404) if action == 'block': f = CardBlockForm(request.form, blocked_at=now()) if action == 'unblock': f = CardUnblockForm(request.form, unblocked_at=now()) if 'cancel' in request.form.keys(): return True # redirect elif request.method == "POST" and f.validate(): if action == 'block': blocked_at = datetime.datetime.combine( f.blocked_at.data, datetime.time()) blocked_at = make_start_date(date=blocked_at) result = card.block(f.reason.data, blocked_at) if result: card.save() flash("%s blocked" % card.key) return True # redirect if action == 'unblock': unblocked_at = datetime.datetime.combine( f.unblocked_at.data, datetime.time()) unblocked_at = make_end_date(date=unblocked_at) result = card.unblock(unblocked_at) if result: card.save() flash("%s unblocked" % card.key) return True # redurect context = { 'title': "%s a card" % (action.capitalize(), ), 'action': action, 'card': card, 'form': f, 'updated_at': datetime.datetime.now(), 'version': VERSION, } return render_template('card-block.html', **context)
def card_block(key): try: card = Kard.objects.get(key=key) action = "block" if card.blocked: action = "unblock" except Kard.DoesNotExist: abort(404) if action == "block": f = CardBlockForm(request.form, blocked_at=now()) if action == "unblock": f = CardUnblockForm(request.form, unblocked_at=now()) if "cancel" in request.form.keys(): return True # redirect elif request.method == "POST" and f.validate(): if action == "block": blocked_at = datetime.datetime.combine(f.blocked_at.data, datetime.time()) blocked_at = make_start_date(date=blocked_at) result = card.block(f.reason.data, blocked_at) if result: card.save() flash("%s blocked" % card.key) return True # redirect if action == "unblock": unblocked_at = datetime.datetime.combine(f.unblocked_at.data, datetime.time()) unblocked_at = make_end_date(date=unblocked_at) result = card.unblock(unblocked_at) if result: card.save() flash("%s unblocked" % card.key) return True # redurect context = { "title": "%s a card" % (action.capitalize(),), "action": action, "card": card, "form": f, "updated_at": datetime.datetime.now(), "version": VERSION, } return render_template("card-block.html", **context)
def duration(self): if self._duration is not None: return self._duration if self.exited is not None: exited = self.exited else: exited = now() delta = exited - self.entered return delta_in_hours(delta)
def _hit_due_date(card): if card.due_date is None: return '' due_date = make_end_date(date=card.due_date) if card.done_date is None: done_date = make_end_date(date=now()) else: done_date = make_end_date(date=card.done_date) return done_date <= due_date
def make_card(self, **kwargs): from kardboard.util import now key = self._make_unique_key() fields = { 'key': "CMSAD-%s" % key, 'title': "Theres always money in the banana stand", 'backlog_date': now() } fields.update(**kwargs) k = self._get_card_class()(**fields) return k
def current_lead_time(self, today=None): """ Caclucation of the number of days between the backlogging of a card and a comparison point (defaults to today). """ if not self.backlog_date: return None if not today: today = now() return business_days_between(self.backlog_date, today)
def cards(self): if self._cards: return self._cards in_progress_q = Q(done_date=None, start_date__exists=True, team__in=self.teams) backlog_q = Q(backlog_date__exists=True, start_date=None, team__in=self.teams) done_q = Q(done_date__gte=now() - relativedelta(days=self.done_days), team__in=self.teams) cards_query = backlog_q | in_progress_q | done_q self._cards = list(Kard.objects.filter(cards_query)) return self._cards
def current_cycle_time(self, today=None): """ Caclucation of the number of days between the start of a card and a comparison point (defaults to today). Returns None if the card hasn't started yet. """ if not self.start_date: return None if not today: today = now() return business_days_between(self.start_date, today)
def current_lead_time(self, today=None): """ Caclucation of the number of days between the backlogging of a card and a comparison point (defaults to today). """ if not self.backlog_date: return None if today is None and self.done_date is None: today = now() elif today is None and self.done_date is not None: today = self.done_date return days_between(self.backlog_date, today)
def report_service_class(group="all", months=None): from kardboard.app import app service_class_order = app.config.get('SERVICE_CLASSES', {}).keys() service_class_order.sort() service_classes = [ app.config['SERVICE_CLASSES'][k] for k in service_class_order ] if months is None: # We want the current report try: scr = ServiceClassSnapshot.objects.get( group=group, ) except ServiceClassSnapshot.DoesNotExist: scr = ServiceClassSnapshot.calculate( group=group, ) time_range = 'current' start_date = make_start_date(date=datetime.datetime.now()) end_date = make_end_date(date=datetime.datetime.now()) else: start = now() months_ranges = month_ranges(start, months) start_date = months_ranges[0][0] end_date = months_ranges[-1][1] try: scr = ServiceClassRecord.objects.get( group=group, start_date=start_date, end_date=end_date, ) except ServiceClassRecord.DoesNotExist: scr = ServiceClassRecord.calculate( group=group, start_date=start_date, end_date=end_date, ) time_range = 'past %s months' % months context = { 'title': "Service classes: %s" % time_range, 'service_classes': service_classes, 'data': scr.data, 'start_date': start_date, 'end_date': end_date, 'updated_at': scr.updated_at, 'version': VERSION, } return render_template('report-service-class.html', **context)
def cards(self): if self._cards: return self._cards in_progress_q = Q(state__in=self.states.in_progress, team__in=self.teams) backlog_q = Q(state__in=self.states.pre_start, team__in=self.teams) done_q = Q(done_date__gte=now() - relativedelta(days=self.done_days), team__in=self.teams) cards_query = backlog_q | in_progress_q | done_q self._cards = list( Kard.objects.filter(cards_query).exclude('_ticket_system_data')) return self._cards
def current_cycle_time(self, today=None): """ Caclucation of the number of days between the start of a card and a comparison point (defaults to today). Returns None if the card hasn't started yet. """ if not self.start_date: return None if today is None and self.done_date is None: today = now() elif today is None and self.done_date is not None: today = self.done_date return days_between(self.start_date, today)
def _date(self, dtype, date=None, days=0): from kardboard.util import make_end_date, make_start_date from kardboard.util import now if not date: date = now() if dtype == 'start': date = make_start_date(date=date) elif dtype == 'end': date = make_end_date(date=date) date = date + relativedelta(days=days) return date
def _date(self, dtype, date=None, days=0): from kardboard.util import make_end_date, make_start_date from kardboard.util import now if not date: date = now() if dtype == "start": date = make_start_date(date=date) elif dtype == "end": date = make_end_date(date=date) date = date + relativedelta(days=days) return date
def report_service_class(group="all", months=None): from kardboard.app import app service_class_order = app.config.get('SERVICE_CLASSES', {}).keys() service_class_order.sort() service_classes = [ app.config['SERVICE_CLASSES'][k] for k in service_class_order ] if months is None: # We want the current report try: scr = ServiceClassSnapshot.objects.get(group=group, ) except ServiceClassSnapshot.DoesNotExist: scr = ServiceClassSnapshot.calculate(group=group, ) time_range = 'current' start_date = make_start_date(date=datetime.datetime.now()) end_date = make_end_date(date=datetime.datetime.now()) else: start = now() months_ranges = month_ranges(start, months) start_date = months_ranges[0][0] end_date = months_ranges[-1][1] try: scr = ServiceClassRecord.objects.get( group=group, start_date=start_date, end_date=end_date, ) except ServiceClassRecord.DoesNotExist: scr = ServiceClassRecord.calculate( group=group, start_date=start_date, end_date=end_date, ) time_range = 'past %s months' % months context = { 'title': "Service classes: %s" % time_range, 'service_classes': service_classes, 'data': scr.data, 'start_date': start_date, 'end_date': end_date, 'updated_at': scr.updated_at, 'version': VERSION, } return render_template('report-service-class.html', **context)
def kard_post_save(cls, sender, document, **kwargs): observed_card = document try: # This could be a freshly created card, so create a log for it sl, created = cls.objects.get_or_create(auto_save=False, card=observed_card, state=observed_card.state) if created: sl.entered = now() except cls.MultipleObjectsReturned: sl = cls.objects.filter( card=observed_card, state=observed_card.state)[0] sl.service_class=observed_card.service_class.get('name') sl.save()
def cards(self): if self._cards: return self._cards in_progress_q = Q( state__in=self.states.in_progress, team__in=self.teams) backlog_q = Q( state__in=self.states.pre_start, team__in=self.teams) done_q = Q(done_date__gte=now() - relativedelta(days=self.done_days), team__in=self.teams) cards_query = backlog_q | in_progress_q | done_q self._cards = list( Kard.objects.filter(cards_query).exclude('_ticket_system_data') ) return self._cards
def _fixtures_for_test_current_struct(self): from kardboard.util import now from kardboard.util import relativedelta # Speedy cards for i in xrange(0, 5): k = self.make_card( _service_class='Speedy', backlog_date=now() - relativedelta(days=3), start_date=now() - relativedelta(days=1), ) k.save() # Normal cards for i in xrange(0, 10): k = self.make_card( _service_class='Normal', backlog_date=now(), start_date=now() + relativedelta(days=10), ) k.save() # No class cards for i in xrange(0, 5): k = self.make_card( backlog_date=now(), start_date=now() + relativedelta(days=10), ) k.save() for i in xrange(0, 3): k = self.make_card(_service_class='Normal', backlog_date=now(), start_date=now() + relativedelta(days=8), done_date=now() + relativedelta(days=15)) k.save()
def cards(self): if self._cards: return self._cards in_progress_q = Q(state__in=self.states.in_progress, team__in=self.teams) ordered_backlog_q = Q(state=self.states.backlog, team__in=self.teams, priority__exists=True) unordered_backlog_q = Q(state=self.states.backlog, team__in=self.teams, priority__exists=False) total_backlog_q = Q(state=self.states.backlog, team__in=self.teams) done_q = Q(done_date__gte=now() - relativedelta(days=self.done_days), team__in=self.teams) if self.backlog_limit: ordered_backlog_cards = Kard.objects.filter( ordered_backlog_q).order_by('priority', 'created_at') ordered_backlog_cards = ordered_backlog_cards.limit( self.backlog_limit).exclude('_ticket_system_data') unordered_backlog_cards = [] if len(ordered_backlog_cards) < self.backlog_limit: unordered_backlog_cards = Kard.objects.filter( unordered_backlog_q).order_by('created_at') unordered_backlog_cards = unordered_backlog_cards.limit( self.backlog_limit).exclude('_ticket_system_data') backlog_cards = list(ordered_backlog_cards) + list( unordered_backlog_cards) backlog_cards = backlog_cards[:self.backlog_limit] cards_query = in_progress_q | done_q cards = list( Kard.objects.filter(cards_query).exclude( '_ticket_system_data')) self._cards = backlog_cards + cards else: cards_query = total_backlog_q | in_progress_q | done_q self._cards = list( Kard.objects.filter(cards_query).exclude( '_ticket_system_data')) return self._cards
def kard_post_save(cls, sender, document, **kwargs): observed_card = document # Is there a currently open state log logs = cls.objects.filter( card=observed_card, state=observed_card.state, exited__exists=False, ).order_by('-entered') if len(logs) == 0: sl = cls( card=observed_card, state=observed_card.state, entered=now() ) else: sl = logs[0] sl.service_class = observed_card.service_class.get('name') sl.save()
def cards(self): if self._cards: return self._cards in_progress_q = Q( state__in=self.states.in_progress, team__in=self.teams) backlog_q = Q( state=self.states.backlog, team__in=self.teams) done_q = Q(done_date__gte=now() - relativedelta(days=self.done_days), team__in=self.teams) if self.backlog_limit: backlog_cards = Kard.objects.filter(backlog_q).exclude('_ticket_system_data').limit(self.backlog_limit) cards_query = in_progress_q | done_q cards = list(Kard.objects.filter(cards_query).exclude('_ticket_system_data')) self._cards = list(backlog_cards) + cards else: cards_query = backlog_q | in_progress_q | done_q self._cards = list(Kard.objects.filter(cards_query).exclude('_ticket_system_data')) return self._cards
def save(self, *args, **kwargs): self.backlog_date = self._convert_dates_to_datetimes(self.backlog_date) self.start_date = self._convert_dates_to_datetimes(self.start_date) self.done_date = self._convert_dates_to_datetimes(self.done_date) if not self.created_at: self.created_at = now() # Auto move to done if self.done_date: states = States() self.in_progress = False self.state = states.done # Auto fill in final cycle and lead time if self.done_date and self.start_date: self._cycle_time = self.cycle_time self._lead_time = self.lead_time if self.blocked: # Do we have a state change? try: k = Kard.objects.only('state').get(key=self.key, ) if k.state != self.state: # Houston we have a state change # Card is blocked and it's state is about to change self.unblock() except Kard.DoesNotExist: #Card isn't saved can't find its previous state pass self._service_class = self.service_class self._version = self.ticket_system.get_version() self.key = self.key.upper() super(Kard, self).save(*args, **kwargs)
def save(self, *args, **kwargs): self.updated_at = now() super(ServiceClassRecord, self).save(*args, **kwargs)
def save(self, *args, **kwargs): self.updated_at = now() super(ServiceClassSnapshot, self).save(*args, **kwargs)