Skip to content

Commit

Permalink
Merge pull request #96 from ODM2/development
Browse files Browse the repository at this point in the history
Release 0.3.0
  • Loading branch information
fryarludwig authored Nov 13, 2017
2 parents 3e0e025 + 5bc97ae commit 12b2d86
Show file tree
Hide file tree
Showing 59 changed files with 3,625 additions and 921 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/src/dataloaderinterface/static/data
/src/WebSDL/.idea
# Created by .ignore support plugin (hsz.mobi)
### Python template
Expand Down
12 changes: 5 additions & 7 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
beautifulsoup4==4.5.1
codegen==1.0
coverage==4.2
Django==1.10.3
django-autocomplete-light==3.2.1
django-bootstrap-form==3.2.1
django-crispy-forms==1.6.1
django-debug-toolbar==1.6
django-discover-runner==1.0
django-filter==1.0.0
django-formset-js==0.5.0
django-jquery-js==2.1.4
django-webtest==1.8.0
django-widget-tweaks==1.4.1
djangorestframework==3.5.3
Markdown==2.6.7
mysqlclient==1.3.9
patterns==0.3
psycopg2==2.7.1
six==1.10.0
sqlparse==0.2.2
waitress==1.0.1
WebOb==1.6.2
WebTest==2.0.23
29 changes: 19 additions & 10 deletions src/WebSDL/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,22 @@
print("The secret key is required in the settings.json file.")
exit(1)

ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
'rest_framework',
'dataloader',
'dataloaderservices',
'dataloaderinterface',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'dataloader',
'dataloaderservices',
'dataloaderinterface',
'widget_tweaks'
]

Expand Down Expand Up @@ -131,14 +131,10 @@

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

LOGIN_URL = 'login'

DATABASE_ROUTERS = ['WebSDL.db_routers.WebSDLRouter']
Expand All @@ -154,4 +150,17 @@

RECAPTCHA_USER_KEY = data["recaptcha_user_key"] if "recaptcha_user_key" in data else ""

RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify"
RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify"


EMAIL_SENDER = data['email_sender'] if 'email_sender' in data else '',

DEFAULT_FROM_EMAIL = EMAIL_SENDER[0] if isinstance(EMAIL_SENDER, tuple) else EMAIL_SENDER

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

EMAIL_SERVER = data['email_host'] if 'email_host' in data else '',

EMAIL_HOST = EMAIL_SERVER[0] if isinstance(EMAIL_SERVER, tuple) else EMAIL_SERVER

DATETIME_FORMAT = "N j, Y, H:m"
2 changes: 1 addition & 1 deletion src/WebSDL/settings/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

INTERNAL_IPS = (
'127.0.0.1',
'129.123.51.198'
)

STATIC_ROOT = os.path.join(os.path.join(BASE_DIR, os.pardir), 'dataloaderinterface', 'static')
STATIC_URL = '/static/'
SITE_URL = ''

Expand Down
28 changes: 28 additions & 0 deletions src/WebSDL/settings/settings_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"secret_key": "",
"host": "-optional",
"recaptcha_secret_key": "-optional",
"recaptcha_user_key": "-optional",
"email_sender": "password.reset@data.envirodiy.org",
"email_host": "mail.usu.edu",
"databases": [
{
"name": "default",
"schema": "",
"engine": "",
"user": "",
"password": "",
"host": "",
"port": ""
},
{
"name": "odm2",
"schema": "",
"engine": "",
"user": "",
"password": "",
"host": "",
"port": ""
}
]
}
21 changes: 16 additions & 5 deletions src/WebSDL/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf import settings
from django.contrib.auth import views as auth_views
from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.core.urlresolvers import reverse_lazy
from django.views.generic.edit import CreateView

from dataloader.models import Organization
from dataloaderinterface.forms import UserRegistrationForm
from dataloaderinterface.views import UserRegistrationView
from dataloaderinterface.views import UserRegistrationView, UserUpdateView


BASE_URL = settings.SITE_URL[1:]

Expand All @@ -34,11 +32,24 @@
'next_page': reverse_lazy('home')
}

password_reset_configuration = {
'post_reset_redirect': 'password_reset_done'
}

password_done_configuration = {
'post_reset_redirect': 'password_reset_complete'
}

urlpatterns = [
url(r'^' + BASE_URL + 'password-reset/$', auth_views.password_reset, password_reset_configuration, name='password_reset'),
url(r'^' + BASE_URL + 'password-reset/done/$', auth_views.password_reset_done, name='password_reset_done'),
url(r'^' + BASE_URL + 'password-reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', auth_views.password_reset_confirm, password_done_configuration, name='password_reset_confirm'),
url(r'^' + BASE_URL + 'password-reset/completed/$', auth_views.password_reset_complete, name='password_reset_complete'),
url(r'^' + BASE_URL + 'admin/', admin.site.urls),
url(r'^' + BASE_URL + 'login/$', auth_views.login, login_configuration, name='login'),
url(r'^' + BASE_URL + 'logout/$', auth_views.logout, logout_configuration, name='logout'),
url(r'^' + BASE_URL + 'register/$', UserRegistrationView.as_view(), name='user_registration'),
url(r'^' + BASE_URL + 'account/$', UserUpdateView.as_view(), name='user_account'),
url(r'^' + BASE_URL + 'api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(BASE_URL, include('dataloaderinterface.urls')),
url(BASE_URL, include('dataloaderservices.urls')),
Expand Down
63 changes: 44 additions & 19 deletions src/dataloader/models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from __future__ import unicode_literals

import inspect
import sys
import uuid

from django.db import models
from django.utils.encoding import python_2_unicode_compatible

from dataloader.querysets import AffiliationQuerySet, RelatedActionManager, ResultManager, \
DataLoggerFileManager, EquipmentModelManager, DataLoggerFileColumnManager, InstrumentOutputVariableManager, \
DataLoggerFileManager, InstrumentOutputVariableManager, \
EquipmentManager, CalibrationReferenceEquipmentManager, EquipmentUsedManager, MaintenanceActionManager, \
RelatedEquipmentManager, CalibrationActionManager, ODM2QuerySet, ActionQuerySet, ActionByQuerySet, \
FeatureActionQuerySet, TimeSeriesValuesQuerySet
FeatureActionQuerySet, TimeSeriesValuesQuerySet, EquipmentModelQuerySet
from django.conf import settings
from django.db import models
from django.utils.encoding import python_2_unicode_compatible


# TODO: function to handle the file upload folder for file fields.
Expand Down Expand Up @@ -120,7 +122,7 @@ class ResultValue(models.Model):
value_datetime_utc_offset = models.IntegerField(db_column='valuedatetimeutcoffset')

def __str__(self):
return '%s %s' % self.value_datetime, self.data_value
return '%s %s' % (self.value_datetime, self.data_value)

def __repr__(self):
return "<%s('%s', '%s', Result['%s', '%s'], '%s')>" % (
Expand All @@ -137,7 +139,7 @@ class ResultValueAnnotation(models.Model):
annotation = models.ForeignKey('Annotation', db_column='annotationid')

def __str__(self):
return '%s %s' % self.value_datetime, self.data_value
return '%s %s' % (self.value_datetime, self.data_value)

def __repr__(self):
return "<%s('%s', Annotation['%s', '%s'], ResultValue['%s', '%s')>" % (
Expand Down Expand Up @@ -191,6 +193,9 @@ class XIntendedComponent(models.Model):
intended_x_spacing = models.FloatField(db_column='intendedxspacing')
intended_x_spacing_unit = models.ForeignKey('Unit', related_name='+', db_column='intendedxspacingunitsid', blank=True, null=True)

class Meta:
abstract = True


class YIntendedComponent(models.Model):
intended_y_spacing = models.FloatField(db_column='intendedyspacing')
Expand Down Expand Up @@ -407,7 +412,7 @@ class Meta:
class Organization(ODM2Model):
organization_id = models.AutoField(db_column='organizationid', primary_key=True)
organization_type = models.ForeignKey('OrganizationType', db_column='organizationtypecv')
organization_code = models.CharField(db_column='organizationcode', max_length=50)
organization_code = models.CharField(db_column='organizationcode', max_length=50, unique=True)
organization_name = models.CharField(db_column='organizationname', max_length=255)
organization_description = models.CharField(db_column='organizationdescription', blank=True, max_length=500)
organization_link = models.CharField(db_column='organizationlink', blank=True, max_length=255)
Expand Down Expand Up @@ -447,7 +452,7 @@ def role_status(self):
return 'Primary contact' if self.is_primary_organization_contact else 'Secondary contact'

def __str__(self):
return '%s (%s) - %s' % (self.person, self.primary_email, self.organization)
return '%s - %s' % (self.person, self.organization)

def __repr__(self):
return "<Affiliation('%s', Person['%s', '%s'], Organization['%s', '%s'], '%s', '%s', '%s')>" % (
Expand Down Expand Up @@ -547,7 +552,7 @@ class SamplingFeature(models.Model):
sampling_feature_id = models.AutoField(db_column='samplingfeatureid', primary_key=True)
sampling_feature_uuid = models.UUIDField(default=uuid.uuid4, editable=False, db_column='samplingfeatureuuid')
sampling_feature_type = models.ForeignKey('SamplingFeatureType', db_column='samplingfeaturetypecv')
sampling_feature_code = models.CharField(db_column='samplingfeaturecode', max_length=50)
sampling_feature_code = models.CharField(db_column='samplingfeaturecode', max_length=50, unique=True)
sampling_feature_name = models.CharField(db_column='samplingfeaturename', blank=True, max_length=255)
sampling_feature_description = models.CharField(db_column='samplingfeaturedescription', blank=True, max_length=500)
sampling_feature_geo_type = models.ForeignKey('SamplingFeatureGeoType', db_column='samplingfeaturegeotypecv', blank=True, null=True)
Expand All @@ -562,6 +567,12 @@ class SamplingFeature(models.Model):
external_identifiers = models.ManyToManyField('ExternalIdentifierSystem', related_name='sampling_features',
through='SamplingFeatureExternalIdentifier')

@property
def latest_updated_result(self):
return self.feature_actions.with_results()\
.filter(results__value_count__gt=0)\
.latest('results__result_datetime').results.first()

def __str__(self):
return '%s %s' % (self.sampling_feature_code, self.sampling_feature_name)

Expand Down Expand Up @@ -690,7 +701,7 @@ class Unit(models.Model):
unit_link = models.CharField(db_column='unitslink', blank=True, max_length=255)

def __str__(self):
return '%s: %s (%s)' % (self.unit_type_id, self.unit_abbreviation, self.unit_name)
return '%s: %s (%s)' % (self.unit_type_id, self.unit_name, self.unit_abbreviation)

def __repr__(self):
return "<Unit('%s', '%s', '%s', '%s')>" % (
Expand All @@ -717,7 +728,7 @@ class Variable(models.Model):
through='VariableExternalIdentifier')

def __str__(self):
return '%s: %s (%s)' % (self.variable_name_id, self.variable_code, self.variable_type_id)
return '%s: %s' % (self.variable_name_id, self.variable_code)

def __repr__(self):
return "<Variable('%s', '%s', '%s', '%s')>" % (
Expand Down Expand Up @@ -777,7 +788,7 @@ class Meta:
@python_2_unicode_compatible
class DataLoggerProgramFile(models.Model):
program_id = models.AutoField(db_column='programid', primary_key=True)
affiliation_id = models.ForeignKey('Affiliation', db_column='affiliationid')
affiliation = models.ForeignKey('Affiliation', db_column='affiliationid', related_name='data_logger_programs')
program_name = models.CharField(db_column='programname', max_length=255)
program_description = models.CharField(db_column='programdescription', blank=True, max_length=500)
program_version = models.CharField(db_column='programversion', blank=True, max_length=50)
Expand All @@ -798,7 +809,7 @@ class Meta:
@python_2_unicode_compatible
class DataLoggerFile(models.Model):
data_logger_file_id = models.AutoField(db_column='dataloggerfileid', primary_key=True)
program = models.ForeignKey('DataLoggerProgramFile', db_column='programid')
program = models.ForeignKey('DataLoggerProgramFile', db_column='programid', related_name='data_logger_files')
data_logger_file_name = models.CharField(db_column='dataloggerfilename', max_length=255)
data_logger_file_description = models.CharField(db_column='dataloggerfiledescription', blank=True, max_length=500)
data_logger_file_link = models.FileField(db_column='dataloggerfilelink', blank=True)
Expand Down Expand Up @@ -833,8 +844,6 @@ class DataLoggerFileColumn(models.Model):
recording_interval_unit = models.ForeignKey('Unit', related_name='recording_interval_data_logger_file_columns', db_column='recordingintervalunitsid', blank=True, null=True)
aggregation_statistic = models.ForeignKey('AggregationStatistic', related_name='data_logger_file_columns', db_column='aggregationstatisticcv', blank=True)

objects = DataLoggerFileColumnManager()

def __str__(self):
return '%s %s' % (self.column_label, self.column_description)

Expand All @@ -861,10 +870,10 @@ class EquipmentModel(models.Model):
output_variables = models.ManyToManyField('Variable', related_name='instrument_models', through='InstrumentOutputVariable')
output_units = models.ManyToManyField('Unit', related_name='instrument_models', through='InstrumentOutputVariable')

objects = EquipmentModelManager()
objects = EquipmentModelQuerySet.as_manager()

def __str__(self):
return '%s: %s' % (self.model_name, self.model_description)
return '%s %s' % (self.model_manufacturer, self.model_name)

def __repr__(self):
return "<EquipmentModel('%s', '%s', '%s', Organization[%s, %s])>" % (
Expand Down Expand Up @@ -1849,7 +1858,7 @@ class Meta:
db_table = 'pointcoverageresults'


class ProfileResult(ExtendedResult, AggregatedComponent, XOffsetComponent, YOffsetComponent, YIntendedComponent, ZIntendedComponent, TimeIntendedComponent):
class ProfileResult(ExtendedResult, AggregatedComponent, XOffsetComponent, YOffsetComponent, ZIntendedComponent, TimeIntendedComponent):
class Meta:
db_table = 'profileresults'

Expand Down Expand Up @@ -2068,3 +2077,19 @@ class Meta:
db_table = 'transectresultvalueannotations'

# endregion


# TODO: make something more sophisticated than this later on
sql_schema_fix = 'odm2].[' # for SQL databases
psql_schema_fix = 'odm2"."' # for postgres databases
clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)
classes = [model for name, model in clsmembers if issubclass(model, models.Model)]
database_manager = settings.DATABASES['odm2']['ENGINE']

for model in classes:
if database_manager == u'sql_server.pyodbc':
model._meta.db_table = sql_schema_fix.upper() + model._meta.db_table
elif database_manager == u'django.db.backends.postgresql_psycopg2':
model._meta.db_table = psql_schema_fix + model._meta.db_table

# can add more fixes there depending on the database engine
9 changes: 4 additions & 5 deletions src/dataloader/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def for_display(self):
return self.select_related('action').prefetch_related('sampling_feature')

def with_results(self):
return self.prefetch_related('results__timeseriesresult__values', 'results__variable')
return self.prefetch_related('results__timeseriesresult__values', 'results__variable', 'results__unit')


class RelatedActionManager(models.Manager):
Expand Down Expand Up @@ -80,10 +80,9 @@ def get_queryset(self):
# region ODM2 Equipment Extension


class EquipmentModelManager(models.Manager):
def get_queryset(self):
queryset = super(EquipmentModelManager, self).get_queryset()
return queryset.prefetch_related('model_manufacturer')
class EquipmentModelQuerySet(ODM2QuerySet):
def for_display(self):
return self.prefetch_related('model_manufacturer')


class InstrumentOutputVariableManager(models.Manager):
Expand Down
2 changes: 1 addition & 1 deletion src/dataloader/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataloader.models import *
from django.test import TestCase

from dataloader.models import *
from dataloader.tests.data import data_manager

models_data = data_manager.test_data['models']['data']
Expand Down
Loading

0 comments on commit 12b2d86

Please sign in to comment.