Skip to content

Commit f040352

Browse files
authored
Merge pull request #169 from willemarcel/develop
Mapping team model & filters + delete users endpoint
2 parents 8cd7a05 + e6f5050 commit f040352

14 files changed

+980
-15
lines changed

osmchadjango/changeset/filters.py

+61
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
13
from django.contrib.gis.geos import Polygon
24
from django.db.models import Count
35

@@ -8,6 +10,7 @@
810
from django_filters.widgets import BooleanWidget
911

1012
from .models import Changeset
13+
from ..users.models import MappingTeam
1114

1215

1316
class ChangesetFilter(GeoFilterSet):
@@ -88,6 +91,25 @@ class ChangesetFilter(GeoFilterSet):
8891
help_text="""If True, it will get only the changesets created by the
8992
users that you blacklisted."""
9093
)
94+
mapping_teams = filters.CharFilter(
95+
name='user',
96+
method='filter_mapping_team',
97+
help_text="""Filter changesets created by users that are on a Mapping
98+
Team. It accepts a list of teams separated by commas."""
99+
)
100+
exclude_teams = filters.CharFilter(
101+
name='user',
102+
method='exclude_mapping_team',
103+
help_text="""Exclude changesets created by users that are on a Mapping
104+
Team. It accepts a list of teams separated by commas."""
105+
)
106+
exclude_trusted_teams = filters.BooleanFilter(
107+
name=None,
108+
method='filter_hide_trusted_teams',
109+
widget=BooleanWidget(),
110+
help_text="""If True, it will exclude the changesets created by the
111+
users that are part of trusted teams."""
112+
)
91113
area_lt = filters.CharFilter(
92114
name=None,
93115
method='filter_area_lt',
@@ -233,6 +255,45 @@ def filter_blacklist(self, queryset, name, value):
233255
else:
234256
return queryset
235257

258+
def get_username_from_teams(self, teams):
259+
users = []
260+
for i in teams.values_list('users', flat=True):
261+
values = i
262+
if type(values) in[str, bytes, bytearray]:
263+
values = json.loads(values)
264+
for e in values:
265+
users.append(e.get('username'))
266+
return users
267+
268+
def filter_mapping_team(self, queryset, name, value):
269+
try:
270+
# added `if team` to avoid empty strings
271+
teams = MappingTeam.objects.filter(
272+
name__in=[team.strip() for team in value.split(',') if team]
273+
)
274+
users = self.get_username_from_teams(teams)
275+
return queryset.filter(user__in=users)
276+
except MappingTeam.DoesNotExist:
277+
return queryset
278+
279+
def exclude_mapping_team(self, queryset, name, value):
280+
try:
281+
teams = MappingTeam.objects.filter(
282+
name__in=[team.strip() for team in value.split(',') if team]
283+
)
284+
users = self.get_username_from_teams(teams)
285+
return queryset.exclude(user__in=users)
286+
except MappingTeam.DoesNotExist:
287+
return queryset
288+
289+
def filter_hide_trusted_teams(self, queryset, name, value):
290+
teams = MappingTeam.objects.filter(trusted=True)
291+
users = self.get_username_from_teams(teams)
292+
if users:
293+
return queryset.exclude(user__in=users)
294+
else:
295+
return queryset
296+
236297
def filter_checked_by(self, queryset, name, value):
237298
if (self.request is None or self.request.user.is_authenticated) and value:
238299
lookup = '__'.join([name, 'name__in'])

osmchadjango/changeset/tests/modelfactories.py

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from ..models import Changeset, SuspicionReasons, UserWhitelist, Tag
1111
from ...feature.models import Feature
12+
from ...users.models import MappingTeam
1213

1314

1415
class UserFactory(factory.django.DjangoModelFactory):
@@ -93,3 +94,13 @@ class Meta:
9394
geojson = json.dumps(
9495
{'properties': {'osm:type': 'node', 'name': 'Test', 'building':'yes'}}
9596
)
97+
98+
99+
class MappingTeamFactory(factory.django.DjangoModelFactory):
100+
class Meta:
101+
model = MappingTeam
102+
103+
name = "Map Company"
104+
trusted = True
105+
created_by = factory.SubFactory(UserFactory)
106+
users = []

osmchadjango/changeset/tests/test_changeset_views.py

+78-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from ..views import ChangesetListAPIView, PaginatedCSVRenderer
1414
from .modelfactories import (
1515
ChangesetFactory, SuspectChangesetFactory, GoodChangesetFactory,
16-
HarmfulChangesetFactory, TagFactory, UserWhitelistFactory
16+
HarmfulChangesetFactory, TagFactory, UserWhitelistFactory,
17+
MappingTeamFactory
1718
)
1819

1920

@@ -148,6 +149,82 @@ def test_blacklisted_filter(self):
148149
self.assertEqual(response.status_code, 200)
149150
self.assertEqual(response.data['count'], 53)
150151

152+
def test_mapping_team_filter(self):
153+
ChangesetFactory(uid=444, user='the_user')
154+
self.team = MappingTeamFactory(
155+
name="TestCompany",
156+
users=json.dumps([{
157+
"username": "test",
158+
"doj": "2017-02-13T00:00:00Z",
159+
"uid": "123123",
160+
"dol": ""
161+
}])
162+
)
163+
self.team_2 = MappingTeamFactory(
164+
name="MapCompany",
165+
trusted=False,
166+
users=json.dumps([{
167+
"username": "the_user",
168+
"doj": "2017-02-13T00:00:00Z",
169+
"uid": "444",
170+
"dol": ""
171+
}])
172+
)
173+
self.client.login(username=self.user.username, password='password')
174+
# mapping_teams tests
175+
response = self.client.get(self.url, {'mapping_teams': self.team.name})
176+
self.assertEqual(response.status_code, 200)
177+
self.assertEqual(response.data['count'], 52)
178+
179+
response = self.client.get(self.url, {'mapping_teams': self.team_2.name})
180+
self.assertEqual(response.status_code, 200)
181+
self.assertEqual(response.data['count'], 1)
182+
183+
response = self.client.get(
184+
self.url,
185+
{'mapping_teams': 'TestCompany,MapCompany'}
186+
)
187+
self.assertEqual(response.status_code, 200)
188+
self.assertEqual(response.data['count'], 53)
189+
190+
response = self.client.get(
191+
self.url,
192+
{'mapping_teams': 'TestCompany, MapCompany'}
193+
)
194+
self.assertEqual(response.status_code, 200)
195+
self.assertEqual(response.data['count'], 53)
196+
197+
# exclude_teams tests
198+
response = self.client.get(self.url, {'exclude_teams': self.team.name})
199+
self.assertEqual(response.status_code, 200)
200+
self.assertEqual(response.data['count'], 1)
201+
202+
response = self.client.get(self.url, {'exclude_teams': self.team_2.name})
203+
self.assertEqual(response.status_code, 200)
204+
self.assertEqual(response.data['count'], 52)
205+
206+
response = self.client.get(
207+
self.url,
208+
{'exclude_teams': 'TestCompany,MapCompany'}
209+
)
210+
self.assertEqual(response.status_code, 200)
211+
self.assertEqual(response.data['count'], 0)
212+
213+
response = self.client.get(
214+
self.url,
215+
{'exclude_teams': 'TestCompany, MapCompany'}
216+
)
217+
self.assertEqual(response.status_code, 200)
218+
self.assertEqual(response.data['count'], 0)
219+
220+
# exclude trusted teams
221+
response = self.client.get(
222+
self.url,
223+
{'exclude_trusted_teams': 'true'}
224+
)
225+
self.assertEqual(response.status_code, 200)
226+
self.assertEqual(response.data['count'], 1)
227+
151228
def test_csv_renderer(self):
152229
self.assertIn(
153230
PaginatedCSVRenderer,

osmchadjango/changeset/tests/test_filters.py

+57-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import date, timedelta
2+
import json
23

34
from django.contrib.gis.geos import Polygon
45
from django.utils import timezone
@@ -9,7 +10,7 @@
910
from .modelfactories import (
1011
ChangesetFactory, SuspectChangesetFactory, UserFactory,
1112
HarmfulChangesetFactory, GoodChangesetFactory, SuspicionReasonsFactory,
12-
TagFactory
13+
TagFactory, MappingTeamFactory
1314
)
1415

1516

@@ -266,6 +267,61 @@ def test_tags_filter(self):
266267
0
267268
)
268269

270+
def test_mapping_team_filter(self):
271+
self.changeset = ChangesetFactory(
272+
user='mapbox_user',
273+
uid=3476,
274+
id=9876,
275+
editor='iD 2.0.2',
276+
comment='My first edit',
277+
)
278+
self.mapbox_team = MappingTeamFactory(
279+
name="Mapbox",
280+
users=json.dumps([{
281+
"username": "mapbox_user",
282+
"doj": "2017-02-13T00:00:00Z",
283+
"uid": "3476",
284+
"dol": ""
285+
},
286+
])
287+
)
288+
self.team = MappingTeamFactory(
289+
users=json.dumps([{
290+
"username": "test",
291+
"doj": "2017-02-13T00:00:00Z",
292+
"uid": "123123",
293+
"dol": ""
294+
}])
295+
)
296+
self.assertEqual(
297+
ChangesetFilter({'mapping_teams': self.team.name}).qs.count(),
298+
3
299+
)
300+
self.assertEqual(
301+
ChangesetFilter({'mapping_teams': self.mapbox_team.name}).qs.count(),
302+
1
303+
)
304+
self.assertEqual(
305+
ChangesetFilter({
306+
'mapping_teams': "{},{}".format(self.team.name, self.mapbox_team.name)
307+
}).qs.count(),
308+
4
309+
)
310+
self.assertEqual(
311+
ChangesetFilter({'exclude_teams': self.team.name}).qs.count(),
312+
2
313+
)
314+
self.assertEqual(
315+
ChangesetFilter(
316+
{'exclude_teams': "{},{}".format(self.team.name, self.mapbox_team.name)}
317+
).qs.count(),
318+
1
319+
)
320+
self.assertEqual(
321+
ChangesetFilter({'exclude_trusted_teams': 'true'}).qs.count(),
322+
1
323+
)
324+
269325

270326
class TestChangesetAreaLowerThan(TestCase):
271327
def setUp(self):

osmchadjango/supervise/views.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
from django.urls import reverse
66

77
from rest_framework.generics import (
8-
ListCreateAPIView, ListAPIView, RetrieveUpdateDestroyAPIView, get_object_or_404
8+
ListCreateAPIView, ListAPIView, RetrieveUpdateDestroyAPIView,
9+
get_object_or_404
910
)
1011
from rest_framework.response import Response
1112
from rest_framework.filters import OrderingFilter
1213
from rest_framework.permissions import (
13-
IsAuthenticated, IsAdminUser, BasePermission, SAFE_METHODS
14+
IsAuthenticated, BasePermission, SAFE_METHODS
1415
)
1516

1617
from ..changeset.serializers import (
1718
ChangesetSerializer, ChangesetSerializerToStaff, ChangesetStatsSerializer
1819
)
1920
from ..changeset.views import StandardResultsSetPagination
20-
from ..changeset.filters import ChangesetFilter
2121
from .models import AreaOfInterest, BlacklistedUser
2222
from .serializers import (
2323
AreaOfInterestSerializer, BlacklistSerializer,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 2.0.9 on 2019-01-23 11:29
2+
3+
from django.conf import settings
4+
import django.contrib.postgres.fields.jsonb
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('users', '0009_auto_20180307_1417'),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='MappingTeam',
18+
fields=[
19+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20+
('name', models.CharField(db_index=True, max_length=255)),
21+
('date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')),
22+
('trusted', models.BooleanField(default=False)),
23+
('users', django.contrib.postgres.fields.jsonb.JSONField(default=list)),
24+
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
25+
],
26+
options={
27+
'verbose_name': 'Mapping Team',
28+
'verbose_name_plural': 'Mapping Teams',
29+
'ordering': ['date'],
30+
},
31+
),
32+
migrations.AlterUniqueTogether(
33+
name='mappingteam',
34+
unique_together={('created_by', 'name')},
35+
),
36+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 2.0.10 on 2019-02-06 12:57
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('users', '0010_auto_20190123_1129'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='mappingteam',
15+
name='name',
16+
field=models.CharField(db_index=True, max_length=255, unique=True),
17+
),
18+
migrations.AlterUniqueTogether(
19+
name='mappingteam',
20+
unique_together=set(),
21+
),
22+
]

osmchadjango/users/models.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33

44
from django.contrib.auth.models import AbstractUser
55
from django.db import models
6-
# from django.utils.translation import ugettext_lazy as _
6+
from django.contrib.postgres.fields import JSONField
77

88

99
class User(AbstractUser):
10-
1110
# First Name and Last Name do not cover name patterns around the globe.
1211
name = models.CharField(
1312
"Name of User", blank=True, max_length=255, db_index=True
@@ -27,3 +26,19 @@ class User(AbstractUser):
2726

2827
def __str__(self):
2928
return self.username
29+
30+
31+
class MappingTeam(models.Model):
32+
name = models.CharField(max_length=255, db_index=True, unique=True)
33+
date = models.DateTimeField("Creation Date", auto_now_add=True)
34+
trusted = models.BooleanField(default=False)
35+
users = JSONField(default=list)
36+
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
37+
38+
def __str__(self):
39+
return '{} by {}'.format(self.name, self.created_by.username)
40+
41+
class Meta:
42+
ordering = ['date']
43+
verbose_name = 'Mapping Team'
44+
verbose_name_plural = 'Mapping Teams'

0 commit comments

Comments
 (0)