launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00013
[Merge] lp:~matsubara/oops-tools/bug-417108-vhost-separation into lp:~launchpad-pqm/oops-tools/trunk
Diogo Matsubara has proposed merging lp:~matsubara/oops-tools/bug-417108-vhost-separation into lp:~launchpad-pqm/oops-tools/trunk.
Requested reviews:
Gary Poster (gary)
Related bugs:
#417108 Send per-vhost OOPS summary
https://bugs.launchpad.net/bugs/417108
This is the first iteration of the vhost separation bug.
This branch adds new models to help map vhost and oops prefixes to teams. Adds fixture for initial data required by the new models. Uses the Django infrastructure to enable dynamically generated oops summaries and also includes the initial work to figure out which team an oops belong to.
--
https://code.launchpad.net/~matsubara/oops-tools/bug-417108-vhost-separation/+merge/14034
Your team Launchpad code reviewers is subscribed to branch lp:~launchpad-pqm/oops-tools/trunk.
=== modified file 'Makefile'
--- Makefile 2009-08-29 23:26:29 +0000
+++ Makefile 2009-10-27 15:20:25 +0000
@@ -29,3 +29,7 @@
update_db:
DJANGO_SETTINGS_MODULE=oopstools.settings PYTHONPATH=$(PWD):$(PWD)/lib/:$(PWD)/oopstools/oops/ $(PYTHON) update_db.py
+
+load_sample_data:
+ $(PYTHON) oopstools/manage.py loaddata initial_data.json && DJANGO_SETTINGS_MODULE=oopstools.settings PYTHONPATH=$(PWD):$(PWD)/lib/:$(PWD)/oopstools/oops/ $(PYTHON) load_sample_data.py
+
=== added file 'load_sample_data.py'
--- load_sample_data.py 1970-01-01 00:00:00 +0000
+++ load_sample_data.py 2009-10-27 15:20:25 +0000
@@ -0,0 +1,24 @@
+# Load sample data in the database defined in settings
+# This script is useful if you want to test things locally via make run
+
+import sys
+
+from datetime import datetime
+
+from django.conf import settings
+from oopstools.oops.views import getOopsStore
+
+
+def main(argv):
+ oops_store = getOopsStore()
+ start = datetime(2007,01,01)
+ end = datetime(2009,12,12)
+ count = 0
+ for oops in oops_store.search(start, end):
+ count += 1
+ print "Loaded %s" % oops.oopsid
+ print "Loaded %d OOPSes into database: %s" % (
+ count, settings.DATABASE_NAME)
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
=== added directory 'oopstools/oops/fixtures'
=== added file 'oopstools/oops/fixtures/initial_data.json'
--- oopstools/oops/fixtures/initial_data.json 1970-01-01 00:00:00 +0000
+++ oopstools/oops/fixtures/initial_data.json 2009-10-27 15:20:25 +0000
@@ -0,0 +1,427 @@
+[
+ {
+ "model": "oops.appinstance",
+ "pk": 1,
+ "fields": { "appinstance": "lpnet" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 2,
+ "fields": { "appinstance": "edge" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 3,
+ "fields": { "appinstance": "staging" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 4,
+ "fields": { "appinstance": "beta" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 5,
+ "fields": { "appinstance": "demo" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 6,
+ "fields": { "appinstance": "demo-librarian" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 7,
+ "fields": { "appinstance": "dogfood" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 8,
+ "fields": { "appinstance": "ppa" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 9,
+ "fields": { "appinstance": "production" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 10,
+ "fields": { "appinstance": "production-mailman" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 11,
+ "fields": { "appinstance": "production-xmlrpc-private" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 12,
+ "fields": { "appinstance": "shipit" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 13,
+ "fields": { "appinstance": "shipit-login" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 14,
+ "fields": { "appinstance": "staging-mailman" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 15,
+ "fields": { "appinstance": "staging-setup" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 16,
+ "fields": { "appinstance": "u1-login" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 17,
+ "fields": { "appinstance": "wildcherry" }
+ },
+ {
+ "model": "oops.appinstance",
+ "pk": 18,
+ "fields": { "appinstance": "development" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 1,
+ "fields": { "team": "bugs" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 2,
+ "fields": { "team": "code" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 3,
+ "fields": { "team": "foundations" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 4,
+ "fields": { "team": "registry" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 5,
+ "fields": { "team": "soyuz" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 6,
+ "fields": { "team": "translations" }
+ },
+ {
+ "model": "oops.team",
+ "pk": 7,
+ "fields": { "team": "unknown" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 1,
+ "fields": { "prefix": "A", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 2,
+ "fields": { "prefix": "B", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 3,
+ "fields": { "prefix": "C", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 4,
+ "fields": { "prefix": "D", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 5,
+ "fields": { "prefix": "E", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 6,
+ "fields": { "prefix": "F", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 7,
+ "fields": { "prefix": "G", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 8,
+ "fields": { "prefix": "H", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 9,
+ "fields": { "prefix": "I", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 10,
+ "fields": { "prefix": "J", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 11,
+ "fields": { "prefix": "K", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 12,
+ "fields": { "prefix": "L", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 13,
+ "fields": { "prefix": "M", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 14,
+ "fields": { "prefix": "N", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 15,
+ "fields": { "prefix": "EA", "appinstance": "2" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 16,
+ "fields": { "prefix": "EB", "appinstance": "2" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 17,
+ "fields": { "prefix": "EC", "appinstance": "2" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 18,
+ "fields": { "prefix": "ED", "appinstance": "2" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 19,
+ "fields": { "prefix": "S", "appinstance": "3" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 20,
+ "fields": { "prefix": "BA", "appinstance": "4" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 21,
+ "fields": { "prefix": "BB", "appinstance": "4" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 22,
+ "fields": { "prefix": "BC", "appinstance": "4" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 23,
+ "fields": { "prefix": "DEMO", "appinstance": "5" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 24,
+ "fields": { "prefix": "XMLP", "appinstance": "11" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 25,
+ "fields": { "prefix": "DF", "appinstance": "7" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 26,
+ "fields": { "prefix": "CEMAIL", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 27,
+ "fields": { "prefix": "SEMAIL", "appinstance": "3" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 28,
+ "fields": { "prefix": "CCW", "appinstance": "1" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 29,
+ "fields": { "prefix": "SCW", "appinstance": "3" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 30,
+ "fields": { "prefix": "MMX", "appinstance": "10" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 31,
+ "fields": { "prefix": "SMMX", "appinstance": "3" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 32,
+ "fields": { "prefix": "X", "appinstance": "18" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 33,
+ "fields": { "prefix": "SMS", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 34,
+ "fields": { "prefix": "SMPM", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 35,
+ "fields": { "prefix": "SMPU", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 36,
+ "fields": { "prefix": "SMPI", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 37,
+ "fields": { "prefix": "SMI", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 38,
+ "fields": { "prefix": "SMPSSH", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 39,
+ "fields": { "prefix": "CIW", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 40,
+ "fields": { "prefix": "CID", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 41,
+ "fields": { "prefix": "CMP", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 42,
+ "fields": { "prefix": "MPCJ", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 43,
+ "fields": { "prefix": "SMPCJ", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 44,
+ "fields": { "prefix": "BM", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 45,
+ "fields": { "prefix": "UPD", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 46,
+ "fields": { "prefix": "UPDS", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 47,
+ "fields": { "prefix": "SRSBR", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 48,
+ "fields": { "prefix": "RSBR", "appinstance": "9" }
+ },
+ {
+ "model": "oops.prefix",
+ "pk": 49,
+ "fields": { "prefix": "SCMP", "appinstance": "9" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 1,
+ "fields": { "vhost": "mainsite", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 2,
+ "fields": { "vhost": "api", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 3,
+ "fields": { "vhost": "blueprints", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 4,
+ "fields": { "vhost": "code", "team": "2" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 5,
+ "fields": { "vhost": "translations", "team": "6" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 6,
+ "fields": { "vhost": "bugs", "team": "1" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 7,
+ "fields": { "vhost": "answers", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 8,
+ "fields": { "vhost": "openid", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 9,
+ "fields": { "vhost": "xmlrpc", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 10,
+ "fields": { "vhost": "feeds", "team": "3" }
+ },
+ {
+ "model": "oops.vhost",
+ "pk": 11,
+ "fields": { "vhost": "login", "team": "3" }
+ }
+]
=== modified file 'oopstools/oops/models.py'
--- oopstools/oops/models.py 2009-10-22 19:21:31 +0000
+++ oopstools/oops/models.py 2009-10-27 15:20:25 +0000
@@ -10,6 +10,18 @@
from django.db import models
+
+oops_re = re.compile(
+ r'''
+ ^
+ (?:(?P<date>\d{4}-\d{2}-\d{2})/)?
+ (?:OOPS-)?
+ (?P<dse>\d*)
+ (?P<oopsprefix>[a-z-]+)
+ (?P<id>\d+)
+ ,?
+ $''', re.IGNORECASE | re.VERBOSE)
+
# This pattern is intended to match the majority of search engines I
# built up this list by checking what was accessing
# https://launchpad.net/robots.txt, so it is probably incomplete. It
@@ -76,6 +88,34 @@
pass
+class Team(models.Model):
+ """The team an oops belong to."""
+ # code, bugs, foundations, registry, etc.
+ team = models.CharField(unique=True, max_length=20)
+
+
+class Vhost(models.Model):
+ """Mapping between vhosts and teams."""
+ # mainsite, api, blueprints, code, translations, bugs,
+ # answers, openid, xmlrpc, feeds
+ vhost = models.CharField(unique=True, max_length=20)
+ team = models.ForeignKey(Team)
+
+
+class AppInstance(models.Model):
+ """The app instance an oops belongs to."""
+ # lpnet, edge, staging, etc.
+ appinstance = models.CharField(unique=True, max_length=20)
+
+
+class Prefix(models.Model):
+ """Oops prefixes as defined by the app instance configuration."""
+ # A, B, C, EA, EB, EC, BM, RSBR, etc.
+ prefix = models.CharField(unique=True, max_length=20)
+ appinstance = models.ForeignKey(AppInstance)
+ team = models.ForeignKey(Team, null=True)
+
+
class OopsInfestation(models.Model):
"""A group of OOPS reports linked to a bug."""
bug = models.PositiveIntegerField(null=True)
@@ -98,6 +138,8 @@
user_agent = models.CharField(max_length=200, null=True)
most_expensive_statement = models.CharField(max_length=200, null=True)
informational = models.NullBooleanField()
+ team = models.ForeignKey(Team)
+ appinstance = models.ForeignKey(AppInstance)
@property
def exception_type(self):
@@ -112,6 +154,48 @@
"""
return _robot_pat.search(self.user_agent) is not None
+ @property
+ def prefix(self):
+ """Return the prefix for self.oopsid."""
+ oops_prefix = oops_re.match(self.oopsid).group('oopsprefix')
+ assert oops_prefix, "Couldn't find oops prefix for %s" % self.oopsid
+ return oops_prefix
+
+ def guess_team(self):
+ """Guess which team this OOPS belongs to."""
+ #XXX matsubara: guess_team() and _get_team_by_prefix are basically
+ # answering the question: what team this oops belongs to? but they
+ # have different answers. Gary suggests that we should have one
+ # way of answering this.
+ if self.url and not self.url == 'None':
+ # Guess team by vhost.
+ type, path = urllib.splittype(self.url)
+ host, foo = urllib.splithost(path)
+ vhost = host.split('.', 1)[0]
+ try:
+ return Vhost.objects.get(vhost__exact=vhost).team
+ except Vhost.DoesNotExist:
+ # Couldn't find a team for this vhost. Assume it's
+ # vhost-less url. So it should go to foundations for now.
+ # XXX: matsubara: Need better heuristics here because
+ # vhost-less may also mean soyuz or registry
+ return Team.objects.get(team__exact='foundations')
+ else:
+ # Since the oops has no url data, guess team by prefix.
+ return self._get_team_by_prefix()
+
+ def _get_team_by_prefix(self):
+ """Find out the team this OOPS belongs to by prefix."""
+ team = Prefix.objects.get(prefix__iexact=self.prefix).team
+ if not team:
+ # Couldn't guess team by prefix. Assume unknown.
+ return Team.objects.get(team__exact='unknown')
+ return team
+
+ def get_appinstance(self):
+ """Return the AppInstance this OOPS belongs to."""
+ return Prefix.objects.get(prefix__iexact=self.prefix).appinstance
+
class OopsReport:
"""An OOPS report parsed from the filesystem."""
@@ -225,6 +309,8 @@
user_agent=self.user_agent,
most_expensive_statement=self.most_expensive_statement,
informational=self.informational)
+ oops_meta_data.team = oops_meta_data.guess_team()
+ oops_meta_data.appinstance = oops_meta_data.get_appinstance()
oops_meta_data.save()
return oops_meta_data
else:
@@ -417,8 +503,8 @@
"""An oops from the code hosting system."""
prefixes = ["SMS", "SMPM", "SMPU", "SMPI", "SMI", "SMPSSH",
- "CIW", "CID", "CMP", "MPCJ", "SMPCJ", "BM", "UPD", "UPDS", "SRSBR",
- "RSBR"]
+ "CIW", "CID", "CMP", "MPCJ", "SCMP", "SMPCJ", "BM", "UPD", "UPDS",
+ "SRSBR", "RSBR"]
def process_evalue(self):
processed_evalue = OopsReport.process_evalue(self)
=== modified file 'oopstools/oops/oopsstore.py'
--- oopstools/oops/oopsstore.py 2009-08-29 23:26:29 +0000
+++ oopstools/oops/oopsstore.py 2009-10-27 15:20:25 +0000
@@ -7,25 +7,14 @@
import datetime
import itertools
-from oopstools.oops.models import CodeHostingOops, WebAppOops, OopsReadError
+from oopstools.oops.models import (
+ CodeHostingOops, WebAppOops, OopsReadError, oops_re)
# the section of the OOPS ID before the instance identifier is the
# days since the epoch, which is defined as the start of 2006.
epoch = datetime.datetime(2006, 01, 01, 00, 00, 00)
-oops_re = re.compile(
- r'''
- ^
- (?:(?P<date>\d{4}-\d{2}-\d{2})/)?
- (?:OOPS-)?
- (?P<dse>\d*)
- (?P<oopsprefix>[a-z-]+)
- (?P<id>\d+)
- ,?
- $''', re.IGNORECASE | re.VERBOSE)
-
-
class OopsNotFound(LookupError):
pass
=== modified file 'oopstools/oops/templates/index-launchpad.html'
--- oopstools/oops/templates/index-launchpad.html 2009-09-25 21:39:43 +0000
+++ oopstools/oops/templates/index-launchpad.html 2009-10-27 15:20:25 +0000
@@ -44,6 +44,8 @@
<label for="staging">staging</label>
</input>
+ <label for="team">Team:</label><input type="text" name="team" />
+
<input type="submit" value="Summary" />
</form>
</div>
=== modified file 'oopstools/oops/test/test_summary_page.txt'
--- oopstools/oops/test/test_summary_page.txt 2009-09-25 21:39:43 +0000
+++ oopstools/oops/test/test_summary_page.txt 2009-10-27 15:20:25 +0000
@@ -1,14 +1,35 @@
+ #XXX matsubara: this tests needs further work to actually mean something.
+ # Currently it only checks that the django infrastructure to generate a
+ # summary works.
+
>>> import djangobrowser
>>> b = djangobrowser.Browser()
- >>> b.open('http://localhost/oops')
- >>> b.getControl(name='period').value = ["custom"]
- >>> b.getControl(name='startdate').value = "2008-01-01"
- >>> b.getControl(name='enddate').value = "2009-01-01"
- >>> b.getControl('Summary').click()
+
+ #>>> b.open('http://localhost/oops')
+ #>>> b.getControl(name='period').value = ["custom"]
+ #>>> b.getControl(name='startdate').value = "2007-02-22"
+ #>>> b.getControl(name='enddate').value = "2009-10-22"
+ #>>> b.getControl(name='team').value = "any"
+ #>>> b.getControl(name='appinstance').value = "lpnet"
+ #>>> b.getControl('Summary').click()
+
+ >>> b.open('http://localhost/summary/?period=custom&startdate=2007-02-22&enddate=2009-10-22&team=any&appinstance=lpnet')
>>> b.url
- 'http://localhost/summary/?period=custom&startdate=2008-01-01&enddate=2009-01-01'
+ 'http://localhost/summary/?period=custom&startdate=2007-02-22&enddate=2009-10-22&team=any&appinstance=lpnet'
>>> b.title
'OOPS Report Summary'
+
+ >>> from helpers import extract_text
+ >>> from BeautifulSoup import BeautifulSoup
+
+ >>> soup = BeautifulSoup(b.contents)
+
+ >>> print extract_text(soup.find('ul'))
+ Log starts: ...
+ Analyzed period: ... days
+ Total exceptions: ...
+ Average exceptions per day: ...
+
=== modified file 'oopstools/oops/views.py'
--- oopstools/oops/views.py 2009-09-25 21:39:43 +0000
+++ oopstools/oops/views.py 2009-10-27 15:20:25 +0000
@@ -7,7 +7,8 @@
from helpers import parsedate
from oopsstore import OopsStore, OopsNotFound
from summaries import WebAppErrorSummary
-from oopstools.oops.models import OopsMetaData, OopsReadError
+from oopstools.oops.models import (
+ AppInstance, OopsMetaData, Team, OopsReadError)
def getOopsStore():
@@ -51,10 +52,31 @@
def summary(request):
startdate = parsedate(request.GET.get("startdate"))
enddate = parsedate(request.GET.get("enddate"))
+ team = request.GET.get('team')
+ appinstance = request.GET.get('appinstance')
summary = WebAppErrorSummary()
- #XXX this will return all oopses in the given period. Need to filter by
- # prefix
- for oops_meta_data in OopsMetaData.objects.filter(date__range=(startdate, enddate)):
+
+ try:
+ team = Team.objects.get(team__exact=team)
+ # listify the result as it'll be used in a IN query
+ team_ids = [team.id]
+ except Team.DoesNotExist:
+ # In case the given team doesn't exist, return all teams.
+ team_ids = [team.id for team in Team.objects.all()]
+
+ try:
+ appinstance = AppInstance.objects.get(
+ appinstance__exact=appinstance)
+ # listify the result as it'll be used in a IN query
+ appinstance_ids = [appinstance.id]
+ except AppInstance.DoesNotExist:
+ # In case the given appinstance doesn't exist, return all instances.
+ appinstance_ids = [
+ appinstance.id for appinstance in AppInstance.objects.all()]
+
+ for oops_meta_data in OopsMetaData.objects.filter(
+ date__range=(startdate, enddate), team__in=team_ids,
+ appinstance__in=appinstance_ids):
summary.processOops(oops_meta_data)
return render_to_response("summary.html", dictionary={"summary": summary})
Follow ups