Skip to content

Commit

Permalink
Merge pull request #524 from SEED-platform/434-develop-labels
Browse files Browse the repository at this point in the history
434 develop labels
  • Loading branch information
mmclark committed Dec 11, 2015
2 parents f924be3 + f691469 commit 713573a
Show file tree
Hide file tree
Showing 83 changed files with 3,068 additions and 1,869 deletions.
8 changes: 6 additions & 2 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"tests"
],
"dependencies": {
"angular":"~1.4.7",
"angular": "~1.4.7",
"angular-bootstrap": "~0.14.3",
"angular-cookies": "~1.4.7",
"angular-mocks": "~1.4.7",
Expand All @@ -24,7 +24,11 @@
"jquery": "~2.1.4",
"jquery-ui": "~1.11.4",
"lodash": "~3.10.1",
"moment": "~2.10.6"
"moment": "~2.10.6",
"angular-xeditable": "~0.1.9",
"ng-tags-input": "~3.0.0",
"angular-ui-notification": "~0.1.0",
"underscore": "~1.8.3"
},
"resolutions": {
"angular": "1.4.7"
Expand Down
14 changes: 14 additions & 0 deletions config/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
'organizations',
'raven.contrib.django',
'tos',
'rest_framework',
)

SEED_CORE_APPS = (
Expand Down Expand Up @@ -270,3 +271,16 @@
"LOWER": 1, # at least 1 Lowercase
"DIGITS": 1, # at least 1 Digit
}

# Django Rest Framework
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'seed.authentication.SEEDAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 25,
}
13 changes: 8 additions & 5 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,22 @@
url(r'^', include('seed.landing.urls', namespace="landing", app_name="landing")),

# accounts/orgs AJAX
url(r'app/accounts/', include('seed.urls.accounts', namespace="accounts", app_name="accounts")),
url(r'^app/accounts/', include('seed.urls.accounts', namespace="accounts", app_name="accounts")),

# projects AJAX
url(r'app/projects/', include('seed.urls.projects', namespace="projects", app_name="projects")),
url(r'^app/projects/', include('seed.urls.projects', namespace="projects", app_name="projects")),

# audit_logs AJAX
url(r'^audit_logs/', include('seed.audit_logs.urls', namespace="audit_logs", app_name="audit_logs")),

# app section
url(r'app/', include('seed.urls.main', namespace="seed", app_name="seed")),
url(r'^app/', include('seed.urls.main', namespace="seed", app_name="seed")),

# api section
url(r'app/api/', include('seed.urls.api', namespace="api", app_name="api")),
url(r'^app/api/', include('seed.urls.api', namespace="api", app_name="api")),

# labels section
url(r'^app/labels', include('seed.urls.labels', namespace="labels", app_name="labels")),

# dataset section
url(r'^data/', include('seed.data_importer.urls', namespace="data_importer", app_name="data_importer")),
Expand All @@ -44,7 +47,7 @@

url(r'^ajax-uploader/', include(ajaxuploader.urls, namespace='ajaxuploader', app_name='ajaxuploader')),

url(r'eula/', include('tos.urls', app_name='tos')),
url(r'^eula/', include('tos.urls', app_name='tos')),

# i18n setlang # TODO: remove i18n support per Nick Serra?
url(r'^i18n/', include('django.conf.urls.i18n')),
Expand Down
1 change: 1 addition & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,4 @@ wsgiref==0.1.2
xlrd==0.9.3
xlwt==0.7.5
xmltodict==0.9.0
djangorestframework==3.3.1
25 changes: 25 additions & 0 deletions seed/authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from rest_framework import authentication
from rest_framework import exceptions


class SEEDAuthentication(authentication.BaseAuthentication):
"""
Django Rest Framework implementation of the
`seed.utils.api.get_api_request_user` functionality to extract the User
from the HTTP_AUTHORIZATION header using an API key.
"""
def authenticate(self, request):
auth_header = request.META.get('HTTP_AUTHORIZATION')

if not auth_header:
return None

try:
from seed.landing.models import SEEDUser as User
username, api_key = auth_header.split(':')
user = User.objects.get(api_key=api_key, username=username)
return user, api_key
except ValueError:
raise exceptions.AuthenticationFailed("Invalid HTTP_AUTHORIZATION Header")
except User.DoesNotExist:
raise exceptions.AuthenticationFailed("Invalid API key")
25 changes: 25 additions & 0 deletions seed/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,28 @@ def wrapper(request, *args, **kwargs):
response['content-length'] = len(data)
return response
return wrapper


def DecoratorMixin(decorator):
"""
Converts a decorator written for a function view into a mixin for a
class-based view.
::
LoginRequiredMixin = DecoratorMixin(login_required)
class MyView(LoginRequiredMixin):
pass
class SomeView(DecoratorMixin(some_decorator),
DecoratorMixin(something_else)):
pass
"""

class Mixin(object):
__doc__ = decorator.__doc__

@classmethod
def as_view(cls, *args, **kwargs):
view = super(Mixin, cls).as_view(*args, **kwargs)
return decorator(view)

Mixin.__name__ = 'DecoratorMixin{0}'.format(decorator.__name__)
return Mixin
46 changes: 46 additions & 0 deletions seed/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import json

from rest_framework import filters

from seed import search


class LabelFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if 'organization_id' in request.query_params:
return queryset.filter(
super_organization_id=request.query_params['organization_id'],
)
return queryset


class BuildingFilterBackend(filters.BaseFilterBackend):
"""
Implements the filtering and searching of buildings as a Django Rest
Framework filter backend.
"""
def filter_queryset(self, request, queryset, view):
# TODO: this needs to be filled in with the same logic that implements
# search/filtering in `seed.views.main.search_buildings`.
params = request.query_params.dict()
# Since this is being passed in as a query string, the object ends up
# coming through as a string.
params['filter_params'] = json.loads(params.get('filter_params', '{}'))

params = search.process_search_params(
params=params,
user=request.user,
is_api_request=True,
)
buildings_queryset = search.orchestrate_search_filter_sort(
params=params,
user=request.user,
)

if request.query_params.get('select_all_checkbox', 'false') == 'true':
pass
elif 'selected_buildings' in request.query_params:
return buildings_queryset.filter(
id__in=request.query_params.getlist('selected_buildings'),
)
return buildings_queryset
20 changes: 20 additions & 0 deletions seed/migrations/0004_auto_20151201_1404.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('seed', '0003_auto_20151105_1539'),
]

operations = [
migrations.AlterField(
model_name='statuslabel',
name='color',
field=models.CharField(default=b'green', max_length=30, verbose_name='compliance_type', choices=[(b'red', 'red'), (b'blue', 'blue'), (b'light blue', 'light blue'), (b'green', 'green'), (b'white', 'white'), (b'orange', 'orange'), (b'gray', 'gray')]),
preserve_default=True,
),
]
23 changes: 23 additions & 0 deletions seed/migrations/0004_noncanonicalprojectbuildings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('seed', '0003_auto_20151105_1539'),
]

operations = [
migrations.CreateModel(
name='NonCanonicalProjectBuildings',
fields=[
('projectbuilding', models.ForeignKey(primary_key=True, serialize=False, to='seed.ProjectBuilding')),
],
options={
},
bases=(models.Model,),
),
]
25 changes: 25 additions & 0 deletions seed/migrations/0005_auto_20151201_1510.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

#some of the project buildings point at building snapshot records that are not canonical buildings
#This probably shouldn't be but before the migration for the label changes record them here
#so the users can be alerted that the label changes will not be applied.
def save_non_canonical_project_buildings(app, schema_editor):
project_building_model = app.get_model("seed", "ProjectBuilding")
canonical_building_model = app.get_model("seed", "CanonicalBuilding")
non_canonical_project_buildings_model = app.get_model("seed", "NonCanonicalProjectBuildings")

for building_with_labels in project_building_model.objects.filter(status_label__isnull=False):
if not canonical_building_model.objects.filter(canonical_snapshot = building_with_labels.building_snapshot.id).exists():
non_canonical_project_buildings_model.objects.create(projectbuilding = building_with_labels)

class Migration(migrations.Migration):

dependencies = [
('seed', '0004_noncanonicalprojectbuildings'),
]

operations = [migrations.RunPython(save_non_canonical_project_buildings),
]
20 changes: 20 additions & 0 deletions seed/migrations/0006_canonicalbuilding_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('seed', '0005_auto_20151201_1510'),
]

operations = [
migrations.AddField(
model_name='canonicalbuilding',
name='labels',
field=models.ManyToManyField(to='seed.StatusLabel'),
preserve_default=True,
),
]
30 changes: 30 additions & 0 deletions seed/migrations/0007_auto_20151201_1515.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

#assuming all building_snapshot_ids in seed_projectbuilding point at the canonical building
#For any records that do no point at canonical buildings the labels will be dropped
#but a record is kept via migration 005_auto_20151201_1510
def move_labels(app, schema_editor):
project_building_model = app.get_model("seed", "ProjectBuilding")
canonical_building_model = app.get_model("seed", "CanonicalBuilding")

for building_with_labels in project_building_model.objects.filter(status_label__isnull=False):
for canonical_building in canonical_building_model.objects.filter(canonical_snapshot = building_with_labels.building_snapshot.id):
canonical_building.labels.add(building_with_labels.status_label)



class Migration(migrations.Migration):


dependencies = [
('seed', '0006_canonicalbuilding_labels'),
]

operations = [ migrations.RunPython(move_labels),
]



18 changes: 18 additions & 0 deletions seed/migrations/0008_remove_projectbuilding_status_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('seed', '0007_auto_20151201_1515'),
]

operations = [
migrations.RemoveField(
model_name='projectbuilding',
name='status_label',
),
]
15 changes: 15 additions & 0 deletions seed/migrations/0009_merge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('seed', '0008_remove_projectbuilding_status_label'),
('seed', '0004_auto_20151201_1404'),
]

operations = [
]

0 comments on commit 713573a

Please sign in to comment.