Skip to content

Commit

Permalink
Merge mozilla#2142
Browse files Browse the repository at this point in the history
2142: Add windows version filter object r=uhlissuh a=uhlissuh

fixes mozilla#2129 

From a slack conversation on this filter object, I took that Mythmon thought the way we currently do the channel (making a database table and populating it with the possible channels) was a good example for doing the windows versions. 

Co-authored-by: Alissa Sobo <[email protected]>
  • Loading branch information
bors[bot] and uhlissuh authored Mar 18, 2020
2 parents 680e222 + 95fc027 commit fe898b1
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/user/filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Filter Objects
.. autoclass:: PrefCompareFilter()
.. autoclass:: PrefUserSetFilter()
.. autoclass:: WindowsBuildNumberFilter()
.. autoclass:: WindowsVersionFilter()


Filter Expressions
Expand Down
45 changes: 45 additions & 0 deletions normandy/recipes/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,50 @@ def to_jexl(self):
return f"(normandy.os.isWindows && {super().to_jexl()})"


class WindowsVersionFilter(BaseComparisonFilter):
"""
Match a user based on what windows version they are running. This filter
creates jexl that compares the windows NT version.
.. attribute:: type
``windows_version``
.. attribute:: value
number, decimal, must be one of the following: 6.1, 6.2, 6.3, 10.0
:example: ``6.1``
.. attribute:: comparison
Options are ``equal``, ``not_equal``, ``greater_than``,
``less_than``, ``greater_than_equal`` and ``less_than_equal``.
:example: ``not_equal``
"""

type = "windows_version"
value = serializers.DecimalField(max_digits=3, decimal_places=1)

@property
def left_of_operator(self):
return "normandy.os.windowsVersion"

def to_jexl(self):
return f"(normandy.os.isWindows && {super().to_jexl()})"

def validate_value(self, value):
from normandy.recipes.models import WindowsVersion

if not WindowsVersion.objects.filter(nt_version=value).exists():
raise serializers.ValidationError(f"Unrecognized windows version slug {value!r}")

return value

@property
def capabilities(self):
return set()


class ProfileCreateDateFilter(BaseFilter):
"""
This filter is meant to distinguish between new and existing users.
Expand Down Expand Up @@ -656,6 +700,7 @@ def capabilities(self):
PrefExistsFilter,
PrefCompareFilter,
PrefUserSetFilter,
WindowsVersionFilter,
]
}

Expand Down
16 changes: 15 additions & 1 deletion normandy/recipes/management/commands/initial_data.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.core.management.base import BaseCommand
from django_countries import countries

from normandy.recipes.models import Channel, Country
from normandy.recipes.models import Channel, Country, WindowsVersion


class Command(BaseCommand):
Expand All @@ -22,6 +22,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
self.add_release_channels()
self.add_countries()
self.add_windows_versions()

def add_release_channels(self):
self.stdout.write("Adding Release Channels...", ending="")
Expand All @@ -41,3 +42,16 @@ def add_countries(self):
for code, name in countries:
Country.objects.update_or_create(code=code, defaults={"name": name})
self.stdout.write("Done")

def add_windows_versions(self):
self.stdout.write("Adding Windows Versions...", ending="")
versions = [
(6.1, "Windows 7"),
(6.2, "Windows 8"),
(6.3, "Windows 8.1"),
(10.0, "Windows 10"),
]

for nt_version, name in versions:
WindowsVersion.objects.update_or_create(nt_version=nt_version, defaults={"name": name})
self.stdout.write("Done")
25 changes: 25 additions & 0 deletions normandy/recipes/migrations/0018_windowsversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.2.10 on 2020-03-18 20:48

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [("recipes", "0017_auto_20191008_1930")]

operations = [
migrations.CreateModel(
name="WindowsVersion",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("nt_version", models.DecimalField(decimal_places=1, max_digits=3)),
("name", models.CharField(max_length=255)),
],
options={"ordering": ("nt_version",)},
)
]
11 changes: 11 additions & 0 deletions normandy/recipes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ def __repr__(self):
return "<Channel {}>".format(self.slug)


class WindowsVersion(models.Model):
nt_version = models.DecimalField(max_digits=3, decimal_places=1)
name = models.CharField(max_length=255)

class Meta:
ordering = ("nt_version",)

def __repr__(self):
return "<Windows Version {}>".format(self.nt_version)


class Country(models.Model):
code = models.CharField(max_length=255, unique=True)
name = models.CharField(max_length=255)
Expand Down
10 changes: 10 additions & 0 deletions normandy/recipes/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
Recipe,
RecipeRevision,
Signature,
WindowsVersion,
)


Expand All @@ -30,6 +31,15 @@ class Meta:
name = "Beta"


class WindowsVersionFactory(factory.DjangoModelFactory):
class Meta:
model = WindowsVersion
django_get_or_create = ("nt_version",)

nt_version = 6.1
name = "Windows 7"


class CountryFactory(factory.DjangoModelFactory):
class Meta:
model = Country
Expand Down
43 changes: 41 additions & 2 deletions normandy/recipes/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@
PrefExistsFilter,
PrefUserSetFilter,
WindowsBuildNumberFilter,
WindowsVersionFilter,
)
from normandy.recipes.tests import (
ChannelFactory,
LocaleFactory,
CountryFactory,
WindowsVersionFactory,
)
from normandy.recipes.tests import ChannelFactory, LocaleFactory, CountryFactory


@pytest.mark.django_db
Expand Down Expand Up @@ -122,7 +128,7 @@ def test_generates_jexl(self):
}


class TestWindowsBuildNumberFiter(FilterTestsBase):
class TestWindowsBuildNumberFilter(FilterTestsBase):
def create_basic_filter(self, value=12345, comparison="equal"):
return WindowsBuildNumberFilter.create(value=value, comparison=comparison)

Expand All @@ -149,6 +155,39 @@ def test_generates_jexl_error_on_bad_comparison(self):
filter.to_jexl()


class TestWindowsVersionFilter(FilterTestsBase):
def create_basic_filter(self, value=6.1, comparison="equal"):
WindowsVersionFactory(nt_version=6.1)

return WindowsVersionFilter.create(value=value, comparison=comparison)

@pytest.mark.parametrize(
"comparison,symbol",
[
("equal", "=="),
("greater_than", ">"),
("greater_than_equal", ">="),
("less_than", "<"),
("less_than_equal", "<="),
],
)
def test_generates_jexl_number_ops(self, comparison, symbol):
filter = self.create_basic_filter(comparison=comparison)
assert (
filter.to_jexl()
== f"(normandy.os.isWindows && normandy.os.windowsVersion {symbol} 6.1)"
)

def test_generates_jexl_error_on_bad_comparison(self):
filter = self.create_basic_filter(comparison="typo")
with pytest.raises(serializers.ValidationError):
filter.to_jexl()

def test_generates_jexl_error_on_bad_version(self):
with pytest.raises(AssertionError):
self.create_basic_filter(value="abcd")


class TestChannelFilter(FilterTestsBase):
def create_basic_filter(self, channels=None):
if channels:
Expand Down

0 comments on commit fe898b1

Please sign in to comment.