Example #1
0
	def test_day_precision(self):
		start_date = FuzzyDate.parse("15 june 1990")
		end_date = FuzzyDate.parse("15 june 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "15th June 1990")

		start_date = FuzzyDate.parse("15 june 1990")
		end_date = FuzzyDate.parse("17 june 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "15th - 17th June 1990")

		start_date = FuzzyDate.parse("30 june 1990")
		end_date = FuzzyDate.parse("2 july 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "30th June - 2nd July 1990")

		start_date = FuzzyDate.parse("1 june 1990")
		end_date = FuzzyDate.parse("1 july 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "1st June - 1st July 1990")

		start_date = FuzzyDate.parse("11 june 1990")
		end_date = FuzzyDate.parse("11 june 1991")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "11th June 1990 - 11th June 1991")
Example #2
0
    def test_default_competition_date(self):
        forever = PartySeries.objects.get(name="Forever")

        forever_longparty = forever.parties.create(
            name="Forever Longparty 2020",
            start_date_date=datetime.date(2020, 5, 14),
            start_date_precision='d',
            end_date_date=datetime.date(2020, 5, 20),
            end_date_precision='d',
        )
        self.assertEqual(forever_longparty.default_competition_date(),
                         FuzzyDate(datetime.date(2020, 5, 15), 'd'))

        forever_futureparty = forever.parties.create(
            name="Forever Futureparty 2020",
            start_date_date=datetime.date(2020, 5, 17),
            start_date_precision='d',
            end_date_date=datetime.date(2020, 5, 20),
            end_date_precision='d',
        )
        self.assertEqual(forever_futureparty.default_competition_date(),
                         FuzzyDate(datetime.date(2020, 5, 17), 'd'))

        forever_fuzzyparty = forever.parties.create(
            name="Forever Fuzzyparty 2020",
            start_date_date=datetime.date(2020, 3, 1),
            start_date_precision='m',
            end_date_date=datetime.date(2020, 3, 1),
            end_date_precision='m',
        )
        self.assertEqual(forever_fuzzyparty.default_competition_date(),
                         FuzzyDate(datetime.date(2020, 3, 1), 'm'))
Example #3
0
    def test_day_precision(self):
        start_date = FuzzyDate.parse("15 june 1990")
        end_date = FuzzyDate.parse("15 june 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "15th June 1990")

        start_date = FuzzyDate.parse("15 june 1990")
        end_date = FuzzyDate.parse("17 june 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "15th - 17th June 1990")

        start_date = FuzzyDate.parse("30 june 1990")
        end_date = FuzzyDate.parse("2 july 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "30th June - 2nd July 1990")

        start_date = FuzzyDate.parse("1 june 1990")
        end_date = FuzzyDate.parse("1 july 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "1st June - 1st July 1990")

        start_date = FuzzyDate.parse("11 june 1990")
        end_date = FuzzyDate.parse("11 june 1991")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "11th June 1990 - 11th June 1991")
Example #4
0
    def setUp(self):
        self.admin = User.objects.create_user(username='******',
                                              password='******')
        self.admin.is_staff = True
        self.admin.save()

        self.user = User.objects.create_user(username='******',
                                             password='******')
        self.production = Production.objects.create(
            title="Second Reality",
            supertype=
            'production',  # FIXME: can't this be set in save() on first create?
        )
        self.uncommented_production = Production.objects.create(
            title="BITS 99",
            supertype='production',
        )
        self.production_comment = Comment.objects.create(
            user=self.user,
            commentable=self.production,
            body="He is not an atomic playboy.",
            created_at=datetime.datetime(2014, 1, 1))
        self.second_production_comment = Comment.objects.create(
            user=self.user,
            commentable=self.production,
            body="On second thoughts, maybe he is.",
            created_at=datetime.datetime(2014, 1, 2))
        self.party_series = PartySeries.objects.create(name="InerciaDemoparty")
        self.party = Party.objects.create(party_series=self.party_series,
                                          start_date=FuzzyDate.parse('2005'),
                                          end_date=FuzzyDate.parse('2005'),
                                          name="InerciaDemoparty 2005")
        self.party_comment = Comment.objects.create(user=self.user,
                                                    commentable=self.party,
                                                    body="I forgot to come.")
Example #5
0
 def test_parse(self):
     self.assertEqual(
         str(FuzzyDate.parse('1993-02-07')).strip(), "7 February 1993")
     self.assertEqual(
         str(FuzzyDate.parse('7 Feb')).strip(), "7 February 2020")
     self.assertEqual(
         str(FuzzyDate.parse('02/07/1993')).strip(), "2 July 1993")
Example #6
0
 def test_str(self):
     self.assertEqual(
         str(FuzzyDate(datetime.date(2020, 8, 1), 'd')).strip(),
         "1 August 2020")
     self.assertEqual(str(FuzzyDate(datetime.date(2020, 8, 1), 'm')),
                      "August 2020")
     self.assertEqual(str(FuzzyDate(datetime.date(2020, 8, 1), 'y')),
                      "2020")
Example #7
0
 def test_short_format(self):
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 1), 'd').short_format(),
         "Aug 2020")
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 1), 'm').short_format(),
         "Aug 2020")
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 1), 'y').short_format(), "2020")
Example #8
0
 def test_numeric_format(self):
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 1), 'd').numeric_format(),
         "2020-08-01")
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 1), 'm').numeric_format(),
         "2020-08")
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 1), 'y').numeric_format(), "2020")
Example #9
0
 def test_date_range_end(self):
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 15), 'd').date_range_end(),
         datetime.date(2020, 8, 15))
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 15), 'm').date_range_end(),
         datetime.date(2020, 8, 31))
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 15), 'y').date_range_end(),
         datetime.date(2020, 12, 31))
Example #10
0
    def test_year_precision(self):
        start_date = FuzzyDate.parse("1990")
        end_date = FuzzyDate.parse("1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "1990")

        start_date = FuzzyDate.parse("1990")
        end_date = FuzzyDate.parse("1991")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "1990 - 1991")
Example #11
0
	def test_year_precision(self):
		start_date = FuzzyDate.parse("1990")
		end_date = FuzzyDate.parse("1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "1990")

		start_date = FuzzyDate.parse("1990")
		end_date = FuzzyDate.parse("1991")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "1990 - 1991")
Example #12
0
 def test_date_range_start(self):
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 15), 'd').date_range_start(),
         datetime.date(2020, 8, 15))
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 15), 'm').date_range_start(),
         datetime.date(2020, 8, 1))
     self.assertEqual(
         FuzzyDate(datetime.date(2020, 8, 15), 'y').date_range_start(),
         datetime.date(2020, 1, 1))
Example #13
0
def prod_soundtracks_without_release_date(request):
	report_name = 'prod_soundtracks_without_release_date'

	productions = Production.objects.raw('''
		SELECT DISTINCT ON (soundtrack.id)
			soundtrack.*,
			production.release_date_date AS suggested_release_date_date,
			production.release_date_precision AS suggested_release_date_precision,
			production.title AS release_detail
		FROM
			productions_production AS soundtrack
			INNER JOIN productions_soundtracklink ON (soundtrack.id = productions_soundtracklink.soundtrack_id)
			INNER JOIN productions_production AS production ON (productions_soundtracklink.production_id = production.id)
		WHERE
			soundtrack.release_date_date IS NULL
			AND soundtrack.id NOT IN (SELECT record_id FROM maintenance_exclusion WHERE report_name = %s)
		ORDER BY
			soundtrack.id, production.release_date_date
	''', [report_name])
	productions = list(productions)
	for production in productions:
		if production.suggested_release_date_date != None:
			production.suggested_release_date = FuzzyDate(production.suggested_release_date_date, production.suggested_release_date_precision)
	return render(request, 'maintenance/production_release_date_report.html', {
		'title': 'Music with productions attached but no release date',
		'productions': productions,
		'report_name': report_name,
		'return_to': reverse('maintenance_prod_soundtracks_without_release_date'),
	})
Example #14
0
def prods_without_release_date_with_placement(request):
	report_name = 'prods_without_release_date_with_placement'

	productions = Production.objects.raw('''
		SELECT DISTINCT ON (productions_production.id)
			productions_production.*,
			parties_party.end_date_date AS suggested_release_date_date,
			parties_party.end_date_precision AS suggested_release_date_precision,
			parties_party.name AS release_detail
		FROM
			productions_production
			INNER JOIN parties_competitionplacing ON (productions_production.id = parties_competitionplacing.production_id)
			INNER JOIN parties_competition ON (parties_competitionplacing.competition_id = parties_competition.id  AND parties_competition.name <> 'Invitation')
			INNER JOIN parties_party ON (parties_competition.party_id = parties_party.id)
		WHERE
			productions_production.release_date_date IS NULL
			AND productions_production.id NOT IN (SELECT record_id FROM maintenance_exclusion WHERE report_name = %s)
		ORDER BY productions_production.id, parties_party.end_date_date
	''', [report_name])

	productions = list(productions)
	for production in productions:
		production.suggested_release_date = FuzzyDate(production.suggested_release_date_date, production.suggested_release_date_precision)
	return render(request, 'maintenance/production_release_date_report.html', {
		'title': 'Productions without a release date but with a party placement attached',
		'productions': productions,
		'report_name': report_name,
		'return_to': reverse('maintenance_prods_without_release_date_with_placement'),
	})
Example #15
0
	def setUp(self):
		self.admin = User.objects.create_user(username='******', password='******')
		self.admin.is_staff = True
		self.admin.save()

		self.user = User.objects.create_user(username='******', password='******')
		self.production = Production.objects.create(
			title="Second Reality",
			supertype='production',  # FIXME: can't this be set in save() on first create?
		)
		self.uncommented_production = Production.objects.create(
			title="BITS 99",
			supertype='production',
		)
		self.production_comment = Comment.objects.create(
			user=self.user,
			commentable=self.production,
			body="He is not an atomic playboy.",
			created_at=datetime.datetime(2014, 1, 1)
		)
		self.second_production_comment = Comment.objects.create(
			user=self.user,
			commentable=self.production,
			body="On second thoughts, maybe he is.",
			created_at=datetime.datetime(2014, 1, 2)
		)
		self.party_series = PartySeries.objects.create(name="InerciaDemoparty")
		self.party = Party.objects.create(
			party_series=self.party_series,
			start_date=FuzzyDate.parse('2005'),
			end_date=FuzzyDate.parse('2005'),
			name="InerciaDemoparty 2005"
		)
		self.party_comment = Comment.objects.create(
			user=self.user,
			commentable=self.party,
			body="I forgot to come."
		)
	def handle_noargs(self, **options):
		for prod in Production.objects.all():
			if prod.release_date:
				continue
			note_match = re.search(r'release date:\s*(.*?)[\.\s]*(\n|$)', prod.notes, re.I)
			if note_match:
				try:
					date = FuzzyDate.parse(note_match.group(1))
					print "%s: %s" % (prod.title, date)
				except ValueError:
					raise ValueError("can't parse date %s for prod %s (%s)" % (note_match.group(1), prod.id, prod.title))
				prod.release_date = date
				prod.notes = replace(prod.notes, note_match.group(0), '')
				prod.save()
Example #17
0
	def setUp(self):
		self.user = User.objects.create(username='******')
		self.production = Production.objects.create(
			title="Second Reality",
			updated_at=datetime.datetime.now()  # FIXME: having to pass updated_at is silly
		)
		self.production_comment = Comment.objects.create(
			user=self.user,
			commentable=self.production,
			body="He is not an atomic playboy."
		)
		self.party_series = PartySeries.objects.create(name="InerciaDemoparty")
		self.party = Party.objects.create(
			party_series=self.party_series,
			start_date=FuzzyDate.parse('2005'),
			end_date=FuzzyDate.parse('2005'),
			name="InerciaDemoparty 2005"
		)
		self.party_comment = Comment.objects.create(
			user=self.user,
			commentable=self.party,
			body="I forgot to come."
		)
Example #18
0
def prods_with_release_date_outside_party(request):
    report_name = 'prods_with_release_date_outside_party'

    productions = Production.objects.raw(
        '''
		SELECT * FROM (
			SELECT DISTINCT ON (productions_production.id)
				productions_production.*,
				parties_party.start_date_date AS party_start_date,
				parties_party.end_date_date AS party_end_date,
				parties_party.end_date_date AS suggested_release_date_date,
				parties_party.end_date_precision AS suggested_release_date_precision,
				parties_party.name AS release_detail,
				parties_party.end_date_precision AS party_end_date_precision
			FROM
				productions_production
				INNER JOIN parties_competitionplacing ON (productions_production.id = parties_competitionplacing.production_id)
				INNER JOIN parties_competition ON (parties_competitionplacing.competition_id = parties_competition.id  AND parties_competition.name <> 'Invitation')
				INNER JOIN parties_party ON (parties_competition.party_id = parties_party.id)
			WHERE
				productions_production.release_date_date IS NOT NULL
				AND productions_production.release_date_precision = 'd'
			ORDER BY productions_production.id, parties_party.end_date_date
		) AS releases
		WHERE
			releases.party_end_date_precision = 'd'
			AND (
				releases.release_date_date < releases.party_start_date - INTERVAL '14 days'
				OR releases.release_date_date > releases.party_end_date + INTERVAL '14 days'
			)
			AND releases.id NOT IN (SELECT record_id FROM maintenance_exclusion WHERE report_name = %s)
	''', [report_name])
    productions = list(productions)
    for production in productions:
        production.suggested_release_date = FuzzyDate(
            production.suggested_release_date_date,
            production.suggested_release_date_precision)

    return render(
        request, 'maintenance/production_release_date_report.html', {
            'title':
            'Productions with a release date more than 14 days away from their release party',
            'productions':
            productions,
            'report_name':
            report_name,
            'return_to':
            reverse('maintenance_prods_with_release_date_outside_party'),
        })
Example #19
0
 def default_competition_date(self):
     if self.end_date and self.end_date.precision == 'd':
         # assume that competitions were held on the penultimate day of the party
         competition_day = self.end_date.date - datetime.timedelta(days=1)
         # ...but if that's in the future, use today instead
         if competition_day > datetime.date.today():
             competition_day = datetime.date.today()
         # ...and if that's before the (known exact) start date of the party, use that instead
         if self.start_date and self.start_date.precision == 'd' and self.start_date.date > competition_day:
             competition_day = self.start_date.date
         return FuzzyDate(competition_day, 'd')
     else:
         # we don't know this party's exact end date, so just use whatever precision of end date
         # we know (if indeed we have one at all)
         return self.end_date
 def handle_noargs(self, **options):
     for prod in Production.objects.all():
         if prod.release_date:
             continue
         note_match = re.search(r'release date:\s*(.*?)[\.\s]*(\n|$)',
                                prod.notes, re.I)
         if note_match:
             try:
                 date = FuzzyDate.parse(note_match.group(1))
                 print "%s: %s" % (prod.title, date)
             except ValueError:
                 raise ValueError(
                     "can't parse date %s for prod %s (%s)" %
                     (note_match.group(1), prod.id, prod.title))
             prod.release_date = date
             prod.notes = replace(prod.notes, note_match.group(0), '')
             prod.save()
Example #21
0
    def test_eq(self):
        d1 = FuzzyDate(datetime.date(2020, 8, 1), 'd')
        d2 = FuzzyDate(datetime.date(2020, 8, 1), 'd')
        m1 = FuzzyDate(datetime.date(2020, 8, 1), 'm')
        m2 = FuzzyDate(datetime.date(2020, 8, 2), 'm')
        y1 = FuzzyDate(datetime.date(2020, 8, 1), 'y')
        y2 = FuzzyDate(datetime.date(2020, 1, 2), 'y')

        self.assertTrue(d1 == d2)
        self.assertFalse(d1 == m1)
        self.assertTrue(m1 == m2)
        self.assertTrue(y1 == y2)
Example #22
0
	def to_python(self, value):
		"""
		Validates that the input can be converted to a date. Returns a
		FuzzyDate object.
		"""
		if value in validators.EMPTY_VALUES:
			return None
		if isinstance(value, FuzzyDate):
			return value
		try:
			result = FuzzyDate.parse(value)
		except ValueError:
			raise ValidationError(self.error_messages['invalid'])

		if result.date.year < 1900:
			raise ValidationError(self.error_messages['invalid'])

		return result
Example #23
0
    def to_python(self, value):
        """
		Validates that the input can be converted to a date. Returns a
		FuzzyDate object.
		"""
        if value in validators.EMPTY_VALUES:
            return None
        if isinstance(value, FuzzyDate):
            return value
        try:
            result = FuzzyDate.parse(value)
        except ValueError:
            raise ValidationError(self.error_messages['invalid'])

        if result.date.year < 1900:
            raise ValidationError(self.error_messages['invalid'])

        return result
Example #24
0
	def test_month_precision(self):
		start_date = FuzzyDate.parse("june 1990")
		end_date = FuzzyDate.parse("june 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "June 1990")

		start_date = FuzzyDate.parse("june 1990")
		end_date = FuzzyDate.parse("july 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "June - July 1990")

		start_date = FuzzyDate.parse("december 1990")
		end_date = FuzzyDate.parse("january 1991")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "December 1990 - January 1991")

		start_date = FuzzyDate.parse("june 1990")
		end_date = FuzzyDate.parse("june 1991")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "June 1990 - June 1991")
Example #25
0
    def test_month_precision(self):
        start_date = FuzzyDate.parse("june 1990")
        end_date = FuzzyDate.parse("june 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "June 1990")

        start_date = FuzzyDate.parse("june 1990")
        end_date = FuzzyDate.parse("july 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "June - July 1990")

        start_date = FuzzyDate.parse("december 1990")
        end_date = FuzzyDate.parse("january 1991")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "December 1990 - January 1991")

        start_date = FuzzyDate.parse("june 1990")
        end_date = FuzzyDate.parse("june 1991")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "June 1990 - June 1991")
Example #26
0
	def test_unequal_precision(self):
		start_date = FuzzyDate.parse("1990")
		end_date = FuzzyDate.parse("july 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "? 1990 - ? July 1990")

		start_date = FuzzyDate.parse("july 1990")
		end_date = FuzzyDate.parse("1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "? July 1990 - ? 1990")

		start_date = FuzzyDate.parse("30 june 1990")
		end_date = FuzzyDate.parse("july 1990")
		result = date_range(start_date, end_date)
		self.assertEqual(result, "30 June 1990 - ? July 1990")
Example #27
0
    def test_unequal_precision(self):
        start_date = FuzzyDate.parse("1990")
        end_date = FuzzyDate.parse("july 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "? 1990 - ? July 1990")

        start_date = FuzzyDate.parse("july 1990")
        end_date = FuzzyDate.parse("1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "? July 1990 - ? 1990")

        start_date = FuzzyDate.parse("30 june 1990")
        end_date = FuzzyDate.parse("july 1990")
        result = date_range(start_date, end_date)
        self.assertEqual(result, "30 June 1990 - ? July 1990")
Example #28
0
    def test_agrees_with(self):
        d1 = FuzzyDate(datetime.date(2020, 8, 1), 'd')
        d2 = FuzzyDate(datetime.date(2020, 8, 1), 'd')
        d3 = FuzzyDate(datetime.date(2020, 8, 2), 'd')
        self.assertTrue(d1.agrees_with(None))
        self.assertTrue(d1.agrees_with(d2))
        self.assertFalse(d1.agrees_with(d3))

        m3 = FuzzyDate(datetime.date(2020, 8, 2), 'm')
        self.assertTrue(d1.agrees_with(m3))

        y1 = FuzzyDate(datetime.date(2020, 1, 1), 'y')
        self.assertTrue(y1.agrees_with(d1))
Example #29
0
	def _has_changed(self, initial, data):
		return initial != FuzzyDate.parse(data)
Example #30
0
 def test_valid(self):
     form = FuzzyDateForm({'date': '15 march 2000'})
     self.assertTrue(form.is_valid())
     self.assertEqual(form.cleaned_data['date'],
                      FuzzyDate(datetime.date(2000, 3, 15), 'd'))
Example #31
0
 def _get_release_date(self):
     if self.release_date_date and self.release_date_precision:
         return FuzzyDate(self.release_date_date,
                          self.release_date_precision)
     else:
         return None
Example #32
0
 def _get_start_date(self):
     return FuzzyDate(self.start_date_date, self.start_date_precision)
Example #33
0
 def _get_end_date(self):
     return FuzzyDate(self.end_date_date, self.end_date_precision)
Example #34
0
 def _has_changed(self, initial, data):
     return initial != FuzzyDate.parse(data)
Example #35
0
 def _get_shown_date(self):
     if self.shown_date_date and self.shown_date_precision:
         return FuzzyDate(self.shown_date_date, self.shown_date_precision)
     else:
         return None
Example #36
0
 def test_invalid_precision(self):
     with self.assertRaises(KeyError):
         FuzzyDate(datetime.date(2020, 8, 1), 'x')
Example #37
0
    def search(self, page_number=1, count=50):
        query = self.cleaned_data['q']

        # Look for filter expressions within query
        filter_expressions = collections.defaultdict(set)
        tag_names = set()

        def apply_filter(match):
            key, val = match.groups()
            if key in RECOGNISED_FILTER_KEYS:
                filter_expressions[key].add(val)
                return ''
            else:
                # the filter has not been recognised;
                # leave the original string intact to be handled as a search term
                return match.group(0)

        for filter_re in (FILTER_RE_ONEWORD, FILTER_RE_SINGLEQUOTE,
                          FILTER_RE_DOUBLEQUOTE):
            query = filter_re.sub(apply_filter, query)

        def apply_tag(match):
            tag_names.add(match.group(1))
            return ''

        query = TAG_RE.sub(apply_tag, query)

        asciified_query = unidecode(query).strip()
        has_search_term = bool(asciified_query)
        if has_search_term:
            psql_query = SearchQuery(unidecode(query))
            clean_query = generate_search_title(query)
            production_filter_q = Q(search_document=psql_query)
            releaser_filter_q = Q(search_document=psql_query)
            party_filter_q = Q(search_document=psql_query)
            bbs_filter_q = Q(search_document=psql_query)
        else:
            production_filter_q = Q()
            releaser_filter_q = Q()
            party_filter_q = Q()
            bbs_filter_q = Q()

        subqueries_to_perform = set(['production', 'releaser', 'party', 'bbs'])

        if 'platform' in filter_expressions or 'on' in filter_expressions:
            subqueries_to_perform &= set(['production'])
            platforms = filter_expressions['platform'] | filter_expressions[
                'on']

            platform_ids = Platform.objects.none().values_list('id', flat=True)
            for platform_name in platforms:
                platform_ids |= (Platform.objects.filter(
                    Q(name__iexact=platform_name)
                    | Q(aliases__name__iexact=platform_name)).values_list(
                        'id', flat=True))

            production_filter_q &= Q(platforms__id__in=list(platform_ids))

        if 'screenshot' in filter_expressions or 'screenshots' in filter_expressions:
            subqueries_to_perform &= set(['production'])

            for flag in filter_expressions['screenshot'] | filter_expressions[
                    'screenshots']:
                if flag in ('yes', 'true'):
                    production_filter_q &= Q(has_screenshot=True)
                elif flag in ('no', 'false'):
                    production_filter_q &= Q(has_screenshot=False)

        if 'by' in filter_expressions or 'author' in filter_expressions:
            subqueries_to_perform &= set(['production'])
            for name in filter_expressions['by'] | filter_expressions['author']:
                clean_name = generate_search_title(name)
                production_filter_q &= (
                    # join back through releaser so that we match any nick variant ever used by the author,
                    # not just the nick used on the prod. Better to err on the side of being too liberal
                    Q(author_nicks__releaser__nicks__variants__search_title=
                      clean_name) |
                    Q(author_affiliation_nicks__releaser__nicks__variants__search_title
                      =clean_name))

        if 'of' in filter_expressions:
            subqueries_to_perform &= set(['releaser'])
            for name in filter_expressions['of']:
                clean_name = generate_search_title(name)
                releaser_filter_q &= Q(
                    is_group=False,
                    group_memberships__group__nicks__variants__search_title=
                    clean_name)

        if 'group' in filter_expressions:
            subqueries_to_perform &= set(['production', 'releaser'])
            for name in filter_expressions['group']:
                clean_name = generate_search_title(name)
                releaser_filter_q &= Q(
                    is_group=False,
                    group_memberships__group__nicks__variants__search_title=
                    clean_name)
                production_filter_q &= (
                    # join back through releaser so that we match any nick variant ever used by the author,
                    # not just the nick used on the prod. Better to err on the side of being too liberal
                    Q(author_nicks__releaser__is_group=True,
                      author_nicks__releaser__nicks__variants__search_title=
                      clean_name) |
                    Q(author_affiliation_nicks__releaser__nicks__variants__search_title
                      =clean_name))

        if tag_names or ('tagged' in filter_expressions):
            subqueries_to_perform &= set(['production'])
            for tag_name in filter_expressions['tagged'] | tag_names:
                production_filter_q &= Q(tags__name=tag_name)

        if 'year' in filter_expressions or 'date' in filter_expressions:
            subqueries_to_perform &= set(['production', 'party'])
            for date_str in filter_expressions['year'] | filter_expressions[
                    'date']:
                try:
                    date_expr = FuzzyDate.parse(date_str)
                except ValueError:
                    continue

                production_filter_q &= Q(
                    release_date_date__gte=date_expr.date_range_start(),
                    release_date_date__lte=date_expr.date_range_end())
                party_filter_q &= Q(
                    end_date_date__gte=date_expr.date_range_start(),
                    start_date_date__lte=date_expr.date_range_end())

        if 'before' in filter_expressions:
            subqueries_to_perform &= set(['production', 'party'])
            for date_str in filter_expressions['before']:
                try:
                    date_expr = FuzzyDate.parse(date_str)
                except ValueError:
                    continue

                production_filter_q &= Q(
                    release_date_date__lt=date_expr.date_range_start())
                party_filter_q &= Q(
                    start_date_date__lt=date_expr.date_range_start())

        if 'until' in filter_expressions:
            subqueries_to_perform &= set(['production', 'party'])
            for date_str in filter_expressions['until']:
                try:
                    date_expr = FuzzyDate.parse(date_str)
                except ValueError:
                    continue

                production_filter_q &= Q(
                    release_date_date__lte=date_expr.date_range_end())
                party_filter_q &= Q(
                    start_date_date__lte=date_expr.date_range_end())

        if 'after' in filter_expressions:
            subqueries_to_perform &= set(['production', 'party'])
            for date_str in filter_expressions['after']:
                try:
                    date_expr = FuzzyDate.parse(date_str)
                except ValueError:
                    continue

                production_filter_q &= Q(
                    release_date_date__gt=date_expr.date_range_end())
                party_filter_q &= Q(
                    end_date_date__gt=date_expr.date_range_end())

        if 'since' in filter_expressions:
            subqueries_to_perform &= set(['production', 'party'])
            for date_str in filter_expressions['since']:
                try:
                    date_expr = FuzzyDate.parse(date_str)
                except ValueError:
                    continue

                production_filter_q &= Q(
                    release_date_date__gte=date_expr.date_range_start())
                party_filter_q &= Q(
                    end_date_date__gte=date_expr.date_range_start())

        if 'type' in filter_expressions:
            requested_types = filter_expressions['type']
            subqueries_from_type = set()
            filter_by_prod_supertype = False
            production_supertypes = []

            for supertype in ('production', 'graphics', 'music'):
                if supertype in requested_types:
                    filter_by_prod_supertype = True
                    production_supertypes.append(supertype)

            if filter_by_prod_supertype:
                subqueries_from_type.add('production')
                production_filter_q &= Q(supertype__in=production_supertypes)

            if 'releaser' in requested_types or 'scener' in requested_types or 'group' in requested_types:
                subqueries_from_type.add('releaser')

                if 'scener' in requested_types and not (
                        'releaser' in requested_types
                        or 'group' in requested_types):
                    releaser_filter_q &= Q(is_group=False)

                if 'group' in requested_types and not (
                        'releaser' in requested_types
                        or 'scener' in requested_types):
                    releaser_filter_q &= Q(is_group=True)

            if 'party' in requested_types:
                subqueries_from_type.add('party')

            if 'bbs' in requested_types:
                subqueries_from_type.add('bbs')

            # assume that any otherwise-unrecognised 'type' values indicate a production type
            production_types = set()
            for val in requested_types:
                if val not in ('production', 'graphics', 'music', 'scener',
                               'group', 'releaser', 'party', 'bbs'):
                    production_types.add(val)

            if production_types:
                prod_type_names_q = Q()
                for name in production_types:
                    prod_type_names_q |= Q(name__iexact=name)
                prod_type_ids = ProductionType.objects.filter(
                    prod_type_names_q).values_list('id', flat=True)

                subqueries_from_type.add('production')
                production_filter_q &= Q(types__in=prod_type_ids)

            subqueries_to_perform &= subqueries_from_type

        # Construct the master search query as a union of subqueries that search
        # one model each. Each subquery yields a queryset of dicts with the following fields:
        # 'type': 'production', 'releaser' or 'party'
        # 'pk': primary key of the relevant object
        # 'exactness': magic number used to prioritise exact/prefix title matches in the ordering:
        #     2 = (the cleaned version of) the title exactly matches (the cleaned verson of) the search query
        #     1 = (the cleaned version of) the title starts with (the cleaned version of) the search query
        #     0 = neither of the above
        # 'rank': search ranking as calculated by postgres search

        # start with an empty queryset
        if has_search_term:
            rank_annotation = SearchRank(F('search_document'), psql_query)
        else:
            rank_annotation = models.Value('', output_field=models.CharField())

        qs = Production.objects.annotate(
            type=models.Value('empty', output_field=models.CharField()),
            exactness=models.Value(0, output_field=models.IntegerField()),
            rank=rank_annotation).values('pk', 'type', 'exactness',
                                         'rank').none()

        if 'production' in subqueries_to_perform:
            # Search for productions

            if has_search_term:
                rank_annotation = SearchRank(F('search_document'), psql_query)
                exactness_annotation = models.Case(
                    models.When(search_title=clean_query,
                                then=models.Value(2)),
                    models.When(search_title__startswith=clean_query,
                                then=models.Value(1)),
                    default=models.Value(0,
                                         output_field=models.IntegerField()),
                    output_field=models.IntegerField())
            else:
                rank_annotation = F('sortable_title')
                exactness_annotation = models.Value(
                    0, output_field=models.IntegerField())

            qs = qs.union(
                Production.objects.annotate(
                    rank=rank_annotation,
                    type=models.Value('production',
                                      output_field=models.CharField()),
                    exactness=exactness_annotation).
                filter(production_filter_q).order_by(
                    # empty order_by to cancel the Production model's native ordering
                ).distinct().values('pk', 'type', 'exactness', 'rank'))

        if 'releaser' in subqueries_to_perform:
            # Search for releasers

            if has_search_term:
                rank_annotation = SearchRank(F('search_document'), psql_query)
                # Exactness test will be applied to each of the releaser's nick variants;
                # take the highest result
                exactness_annotation = models.Max(
                    models.Case(
                        models.When(nicks__variants__search_title=clean_query,
                                    then=models.Value(2)),
                        models.When(nicks__variants__search_title__startswith=
                                    clean_query,
                                    then=models.Value(1)),
                        default=models.Value(
                            0, output_field=models.IntegerField()),
                        output_field=models.IntegerField()))
            else:
                rank_annotation = F('name')
                exactness_annotation = models.Value(
                    0, output_field=models.IntegerField())

            qs = qs.union(
                Releaser.objects.annotate(
                    rank=rank_annotation,
                    type=models.Value('releaser',
                                      output_field=models.CharField()),
                    exactness=exactness_annotation).filter(releaser_filter_q).
                distinct().order_by(
                    # empty order_by to cancel the Releaser model's native ordering
                ).values('pk', 'type', 'exactness', 'rank'))

        if 'party' in subqueries_to_perform:
            # Search for parties

            if has_search_term:
                rank_annotation = SearchRank(F('search_document'), psql_query)
                exactness_annotation = models.Case(
                    models.When(search_title=clean_query,
                                then=models.Value(2)),
                    models.When(search_title__startswith=clean_query,
                                then=models.Value(1)),
                    default=models.Value(0,
                                         output_field=models.IntegerField()),
                    output_field=models.IntegerField())
            else:
                rank_annotation = F('name')
                exactness_annotation = models.Value(
                    0, output_field=models.IntegerField())

            qs = qs.union(
                Party.objects.annotate(
                    rank=rank_annotation,
                    type=models.Value('party',
                                      output_field=models.CharField()),
                    exactness=exactness_annotation,
                ).filter(party_filter_q).order_by(
                    # empty order_by to cancel the Party model's native ordering
                ).values('pk', 'type', 'exactness', 'rank'), )

        if 'bbs' in subqueries_to_perform:
            # Search for BBSes

            if has_search_term:
                rank_annotation = SearchRank(F('search_document'), psql_query)
                exactness_annotation = models.Case(
                    models.When(search_title=clean_query,
                                then=models.Value(2)),
                    models.When(search_title__startswith=clean_query,
                                then=models.Value(1)),
                    default=models.Value(0,
                                         output_field=models.IntegerField()),
                    output_field=models.IntegerField())
            else:
                rank_annotation = F('name')
                exactness_annotation = models.Value(
                    0, output_field=models.IntegerField())

            qs = qs.union(
                BBS.objects.annotate(
                    rank=rank_annotation,
                    type=models.Value('bbs', output_field=models.CharField()),
                    exactness=exactness_annotation,
                ).filter(bbs_filter_q).order_by(
                    # empty order_by to cancel any model-level native ordering
                ).values('pk', 'type', 'exactness', 'rank'), )

        if has_search_term:
            qs = qs.order_by('-exactness', '-rank', 'pk')
        else:
            qs = qs.order_by('-exactness', 'rank', 'pk')

        # Apply pagination to the query before performing the (expensive) real data fetches.

        paginator = Paginator(qs, count)
        # If page request (9999) is out of range, deliver last page of results.
        try:
            page = paginator.page(page_number)
        except (EmptyPage, InvalidPage):
            page = paginator.page(paginator.num_pages)

        # Assemble the results into a plan for fetching the actual models -
        # form a dict that maps model/type to a set of PKs
        to_fetch = {}
        for d in page.object_list:
            to_fetch.setdefault(d['type'], set()).add(d['pk'])

        # now do the fetches, and store the results as a mapping of (type, pk) tuple to object
        fetched = {}

        if 'production' in to_fetch:
            production_ids = to_fetch['production']
            productions = Production.objects.filter(
                pk__in=production_ids).prefetch_related(
                    'author_nicks__releaser',
                    'author_affiliation_nicks__releaser')
            if has_search_term:
                productions = productions.annotate(
                    search_snippet=TSHeadline('notes', psql_query))
            screenshots = Screenshot.select_for_production_ids(production_ids)

            for prod in productions:
                prod.selected_screenshot = screenshots.get(prod.pk)
                # Ignore any search snippets that don't actually contain a highlighted term
                prod.has_search_snippet = has_search_term and '<b>' in prod.search_snippet
                fetched[('production', prod.pk)] = prod

        if 'releaser' in to_fetch:
            releasers = Releaser.objects.filter(
                pk__in=to_fetch['releaser']).prefetch_related(
                    'group_memberships__group__nicks', 'nicks')
            if has_search_term:
                releasers = releasers.annotate(
                    search_snippet=TSHeadline('notes', psql_query))
            for releaser in releasers:
                releaser.has_search_snippet = has_search_term and '<b>' in releaser.search_snippet
                fetched[('releaser', releaser.pk)] = releaser

        if 'party' in to_fetch:
            parties = Party.objects.filter(pk__in=to_fetch['party'])
            if has_search_term:
                parties = parties.annotate(
                    search_snippet=TSHeadline('notes', psql_query))
            for party in parties:
                party.has_search_snippet = has_search_term and '<b>' in party.search_snippet
                fetched[('party', party.pk)] = party

        if 'bbs' in to_fetch:
            bbses = BBS.objects.filter(pk__in=to_fetch['bbs'])
            if has_search_term:
                bbses = bbses.annotate(
                    search_snippet=TSHeadline('notes', psql_query))
            for bbs in bbses:
                bbs.has_search_snippet = has_search_term and '<b>' in bbs.search_snippet
                fetched[('bbs', bbs.pk)] = bbs

        # Build final list in same order as returned by the original results query
        results = []
        for d in page.object_list:
            item = fetched.get((d['type'], d['pk'])) or None
            if item:
                item.search_info = d
                results.append(item)

        return (results, page)
Example #38
0
 def test_passing_fuzzy_date(self):
     form = FuzzyDateForm(
         {'date': FuzzyDate(datetime.date(2000, 3, 15), 'd')})
     self.assertTrue(form.is_valid())
     self.assertEqual(form.cleaned_data['date'],
                      FuzzyDate(datetime.date(2000, 3, 15), 'd'))