├── .coveragerc ├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── Dockerfile ├── GeoKey_updated_documentation.pdf ├── Gruntfile.js ├── LICENSE ├── MANIFEST.in ├── README.rst ├── db ├── Dockerfile └── initdb-hstore.sh ├── docker-compose.yml ├── geokey ├── __init__.py ├── applications │ ├── __init__.py │ ├── base.py │ ├── forms.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150112_1700.py │ │ ├── 0002_auto_20150112_1807.py │ │ ├── 0003_auto_20171024_1400.py │ │ └── __init__.py │ ├── models.py │ ├── tests │ │ ├── __init__.py │ │ ├── model_factories.py │ │ ├── test_managers.py │ │ ├── test_models.py │ │ └── test_views.py │ └── views.py ├── categories │ ├── __init__.py │ ├── base.py │ ├── forms.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150106_1338.py │ │ ├── 0003_datefield.py │ │ ├── 0004_timefield.py │ │ ├── 0005_auto_20150112_1731.py │ │ ├── 0006_category_display_field.py │ │ ├── 0007_auto_20150130_1155.py │ │ ├── 0008_auto_20150130_1216.py │ │ ├── 0010_auto_20150202_1023.py │ │ ├── 0011_auto_20150220_1413.py │ │ ├── 0012_auto_20150223_1311.py │ │ ├── 0013_auto_20150130_1440.py │ │ ├── 0014_auto_20160104_1409.py │ │ ├── 0015_lookupvalue_symbol.py │ │ ├── 0016_multiplelookupvalue_symbol.py │ │ ├── 0017_category_expiry_field.py │ │ ├── 0018_historicalcategory.py │ │ ├── 0019_auto_20181028_1638.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── serializers.py │ ├── templatetags │ │ ├── __init__.py │ │ └── filter_fields.py │ ├── tests │ │ ├── __init__.py │ │ ├── model_factories.py │ │ ├── test_managers.py │ │ ├── test_models.py │ │ ├── test_templatetags.py │ │ └── test_views.py │ └── views.py ├── context_processors.py ├── contributions │ ├── __init__.py │ ├── base.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150106_1338.py │ │ ├── 0003_auto_20150121_1544.py │ │ ├── 0004_auto_20150121_1455.py │ │ ├── 0005_auto_20150202_1135.py │ │ ├── 0006_auto_20150312_1247.py │ │ ├── 0007_auto_20150312_1249.py │ │ ├── 0008_auto_20150312_1508.py │ │ ├── 0009_auto_20150420_1549.py │ │ ├── 0010_auto_20150511_1132.py │ │ ├── 0011_auto_20150527_1255.py │ │ ├── 0012_auto_20150807_0854.py │ │ ├── 0013_auto_20150907_1345.py │ │ ├── 0014_auto_20150907_1345.py │ │ ├── 0015_auto_20150907_1345.py │ │ ├── 0016_audiofile.py │ │ ├── 0017_auto_20160531_1434.py │ │ ├── 0018_historicalcomment.py │ │ ├── 0019_auto_20181020_1915.py │ │ ├── 0020_update_media_and_comments_count.py │ │ └── __init__.py │ ├── models.py │ ├── parsers │ │ ├── __init__.py │ │ └── geojson.py │ ├── renderers │ │ ├── __init__.py │ │ ├── geojson.py │ │ └── kml.py │ ├── serializers.py │ ├── templatetags │ │ ├── __init__.py │ │ └── kml_tags.py │ ├── tests │ │ ├── __init__.py │ │ ├── comments │ │ │ ├── __init__.py │ │ │ ├── test_managers.py │ │ │ ├── test_models.py │ │ │ └── test_views.py │ │ ├── locations │ │ │ ├── __init__.py │ │ │ ├── test_managers.py │ │ │ └── test_views.py │ │ ├── media │ │ │ ├── __init__.py │ │ │ ├── files │ │ │ │ ├── audio_1.mp3 │ │ │ │ ├── audio_10.flac │ │ │ │ ├── audio_12.wma │ │ │ │ ├── audio_2.3gp │ │ │ │ ├── audio_3.opus │ │ │ │ ├── audio_4.m4a │ │ │ │ ├── audio_5.amr │ │ │ │ ├── audio_6.aiff │ │ │ │ ├── audio_7.wav │ │ │ │ ├── audio_8 │ │ │ │ ├── audio_8.opus │ │ │ │ ├── audio_9.aac │ │ │ │ ├── document_1.pdf │ │ │ │ ├── document_2.doc │ │ │ │ ├── image_01.png │ │ │ │ ├── image_02.jpg │ │ │ │ ├── image_03.gif │ │ │ │ ├── image_04.svg │ │ │ │ ├── image_05.tiff │ │ │ │ ├── text_1.txt │ │ │ │ └── video.MOV │ │ │ ├── helpers │ │ │ │ ├── __init__.py │ │ │ │ └── document_helpers.py │ │ │ ├── model_factories.py │ │ │ ├── test_managers.py │ │ │ ├── test_models.py │ │ │ ├── test_serializers.py │ │ │ └── test_views.py │ │ ├── model_factories.py │ │ ├── observations │ │ │ ├── __init__.py │ │ │ ├── test_managers.py │ │ │ ├── test_models.py │ │ │ ├── test_parsers.py │ │ │ ├── test_renderers.py │ │ │ ├── test_templatetags.py │ │ │ └── test_views.py │ │ ├── test_serializers.py │ │ └── test_utils.py │ ├── utils.py │ └── views │ │ ├── __init__.py │ │ ├── base.py │ │ ├── comments.py │ │ ├── locations.py │ │ ├── media.py │ │ └── observations.py ├── core │ ├── __init__.py │ ├── adapters.py │ ├── base.py │ ├── context_processors.py │ ├── decorators.py │ ├── exceptions.py │ ├── middleware.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── serializers.py │ ├── settings │ │ ├── __init__.py │ │ ├── base.py │ │ ├── dev.py │ │ └── prod.py │ ├── signals.py │ ├── templatetags │ │ ├── __init__.py │ │ └── logger.py │ ├── tests │ │ ├── __init__.py │ │ ├── helpers │ │ │ ├── __init__.py │ │ │ ├── image_helpers.py │ │ │ └── render_helpers.py │ │ ├── logger │ │ │ ├── __init__.py │ │ │ ├── test_log_audiofile.py │ │ │ ├── test_log_category.py │ │ │ ├── test_log_comment.py │ │ │ ├── test_log_datefield.py │ │ │ ├── test_log_datetimefield.py │ │ │ ├── test_log_documentfile.py │ │ │ ├── test_log_imagefile.py │ │ │ ├── test_log_location.py │ │ │ ├── test_log_lookupfield.py │ │ │ ├── test_log_multiplelookupfield.py │ │ │ ├── test_log_numericfield.py │ │ │ ├── test_log_observation.py │ │ │ ├── test_log_project.py │ │ │ ├── test_log_subset.py │ │ │ ├── test_log_textfield.py │ │ │ ├── test_log_timefield.py │ │ │ ├── test_log_user.py │ │ │ ├── test_log_usergroup.py │ │ │ └── test_log_videofile.py │ │ ├── test_templatetags.py │ │ ├── test_urls.py │ │ └── test_views.py │ ├── url │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── ajax.py │ │ └── api.py │ ├── urls.py │ └── views.py ├── extensions │ ├── __init__.py │ ├── base.py │ ├── exceptions.py │ ├── mixins.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_base.py │ │ └── test_mixins.py │ └── urls.py ├── projects │ ├── __init__.py │ ├── base.py │ ├── forms.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150106_1338.py │ │ ├── 0003_auto_20150123_1148.py │ │ ├── 0004_auto_20150123_1507.py │ │ ├── 0005_auto_20150202_1041.py │ │ ├── 0006_remove_admins_contact.py │ │ ├── 0007_auto_20160122_1409.py │ │ ├── 0008_historicalproject.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── templatetags │ │ ├── __init__.py │ │ ├── count.py │ │ └── project_attributes.py │ ├── tests │ │ ├── __init__.py │ │ ├── model_factories.py │ │ ├── test_managers.py │ │ ├── test_models.py │ │ ├── test_serializers.py │ │ ├── test_templatetags.py │ │ └── test_views.py │ └── views.py ├── socialinteractions │ ├── __init__.py │ ├── base.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templatetags │ │ ├── __init__.py │ │ └── placeholder_filters.py │ ├── tests │ │ ├── __init__.py │ │ ├── model_factories.py │ │ ├── test_utils.py │ │ └── test_views.py │ ├── utils.py │ └── views.py ├── static │ ├── css │ │ └── admin.css │ ├── img │ │ ├── ajax-loader-blue.gif │ │ ├── ajax-loader.gif │ │ ├── blurry.jpg │ │ ├── play.png │ │ ├── providers │ │ │ ├── facebook.svg │ │ │ └── twitter.svg │ │ └── success.png │ ├── js │ │ ├── admin.control.ajax.js │ │ ├── admin.ui.category.display.js │ │ ├── admin.ui.field.create.js │ │ ├── admin.ui.field.js │ │ ├── admin.ui.field.lookup.js │ │ ├── admin.ui.fileinput.js │ │ ├── admin.ui.filters.data.js │ │ ├── admin.ui.forms.validate.js │ │ ├── admin.ui.project.geographicextent.js │ │ ├── admin.ui.project.js │ │ ├── admin.ui.socialinteractions.twittervalidator.js │ │ ├── admin.ui.sort.js │ │ ├── admin.ui.updater.js │ │ ├── admin.ui.usergroup.permissions.js │ │ ├── admin.ui.usergroup.users.js │ │ └── templates.js │ └── lib │ │ ├── bootstrap-colorpicker │ │ ├── LICENSE │ │ ├── css │ │ │ └── bootstrap-colorpicker.min.css │ │ ├── img │ │ │ └── bootstrap-colorpicker │ │ │ │ ├── alpha-horizontal.png │ │ │ │ ├── alpha.png │ │ │ │ ├── hue-horizontal.png │ │ │ │ ├── hue.png │ │ │ │ └── saturation.png │ │ └── js │ │ │ └── bootstrap-colorpicker.min.js │ │ ├── bootstrap-datetimepicker │ │ ├── LICENSE │ │ ├── css │ │ │ └── bootstrap-datetimepicker.min.css │ │ └── js │ │ │ └── bootstrap-datetimepicker.min.js │ │ ├── bootstrap-fileinput │ │ ├── LICENSE │ │ ├── css │ │ │ └── bootstrap-fileinput.min.css │ │ ├── img │ │ │ ├── loading-sm.gif │ │ │ └── loading.gif │ │ └── js │ │ │ └── bootstrap-fileinput.min.js │ │ ├── bootstrap │ │ ├── LICENSE │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap-theme.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.js │ │ ├── handlebars │ │ ├── LICENSE │ │ └── handlebars.js │ │ ├── jquery │ │ ├── LICENSE │ │ ├── jquery-1.12.0.min.js │ │ └── jquery-ui.min.js │ │ ├── modernizr │ │ ├── LICENSE │ │ └── modernizr-2.6.2-respond-1.1.0.min.js │ │ └── moment │ │ ├── LICENSE │ │ └── moment.min.js ├── subsets │ ├── __init__.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_historicalsubset.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── model_factories.py │ │ ├── test_urls.py │ │ └── test_views.py │ └── views.py ├── superusertools │ ├── __init__.py │ ├── base.py │ ├── mixins.py │ ├── tests │ │ ├── __init__.py │ │ └── test_views.py │ └── views.py ├── templates │ ├── 404.html │ ├── account │ │ ├── account_inactive.html │ │ ├── delete_account.html │ │ ├── email │ │ │ ├── email_confirmation_message.txt │ │ │ ├── email_confirmation_signup_message.txt │ │ │ ├── email_confirmation_signup_subject.txt │ │ │ ├── email_confirmation_subject.txt │ │ │ ├── password_reset_key_message.txt │ │ │ └── password_reset_key_subject.txt │ │ ├── email_confirm.html │ │ ├── login.html │ │ ├── password_change.html │ │ ├── password_reset.html │ │ ├── password_reset_done.html │ │ ├── password_reset_from_key.html │ │ ├── password_reset_from_key_done.html │ │ ├── password_set.html │ │ ├── signup.html │ │ └── verification_sent.html │ ├── applications │ │ ├── application_connected.html │ │ ├── application_create.html │ │ ├── application_overview.html │ │ └── application_settings.html │ ├── base.html │ ├── categories │ │ ├── category_create.html │ │ ├── category_display.html │ │ ├── category_list.html │ │ ├── category_navigation.html │ │ ├── category_overview.html │ │ ├── category_settings.html │ │ ├── field_create.html │ │ └── field_settings.html │ ├── dashboard.html │ ├── geometries │ │ └── placemarks.kml │ ├── handlebars │ │ ├── _created-field.hbs │ │ ├── _datefield.hbs │ │ ├── _datetimefield.hbs │ │ ├── _lookupfield.hbs │ │ ├── _multiplelookupfield.hbs │ │ ├── _numericfield.hbs │ │ ├── _textfield.hbs │ │ ├── _timefield.hbs │ │ ├── createdfield.hbs │ │ ├── field.hbs │ │ ├── fields.hbs │ │ ├── fieldselect.hbs │ │ ├── helpers.js │ │ ├── lookupvalues.hbs │ │ ├── usergroupusers.hbs │ │ └── userstypeaway.hbs │ ├── index.html │ ├── logger │ │ └── logger_list.html │ ├── oauth2_provider │ │ └── authorize.html │ ├── projects │ │ ├── navigation.html │ │ ├── project_attributes.html │ │ ├── project_create.html │ │ ├── project_geographic_extent.html │ │ ├── project_overview.html │ │ ├── project_settings.html │ │ └── projects_involved.html │ ├── snippets │ │ ├── data_fields_rules.html │ │ ├── error.html │ │ ├── footer.html │ │ ├── google_analytics.html │ │ ├── lookup_values.html │ │ ├── messages.html │ │ ├── project_help.html │ │ ├── social_apps.html │ │ └── usergroup_editor.html │ ├── socialaccount │ │ ├── authentication_error.html │ │ ├── login_cancelled.html │ │ └── signup.html │ ├── socialinteractions │ │ ├── socialinteraction_list.html │ │ ├── socialinteraction_post_create.html │ │ ├── socialinteraction_post_settings.html │ │ ├── socialinteraction_pull.html │ │ └── socialinteraction_pull_create.html │ ├── subsets │ │ ├── subset_create.html │ │ ├── subset_data.html │ │ ├── subset_list.html │ │ ├── subset_navigation.html │ │ └── subset_settings.html │ ├── superusertools │ │ ├── manage_inactive_users.html │ │ ├── manage_projects.html │ │ ├── manage_superusers.html │ │ ├── navigation.html │ │ ├── platform_settings.html │ │ ├── provider_list.html │ │ └── provider_overview.html │ ├── templates.js │ └── users │ │ ├── profile.html │ │ ├── usergroup_admins.html │ │ ├── usergroup_create.html │ │ ├── usergroup_data.html │ │ ├── usergroup_list.html │ │ ├── usergroup_navigation.html │ │ ├── usergroup_overview.html │ │ ├── usergroup_permissions.html │ │ └── usergroup_settings.html ├── users │ ├── __init__.py │ ├── forms.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── check_confirm.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150106_1420.py │ │ ├── 0002_auto_20150824_1603.py │ │ ├── 0002_auto_20150904_1113.py │ │ ├── 0003_auto_20150611_1307.py │ │ ├── 0004_auto_20150617_0902.py │ │ ├── 0005_auto_20150825_0933.py │ │ ├── 0006_merge.py │ │ ├── 0007_auto_20151006_1110.py │ │ ├── 0008_historicalusergroup.py │ │ ├── 0009_auto_20180502_1258.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── templatetags │ │ ├── __init__.py │ │ ├── filter_tags.py │ │ └── social.py │ ├── tests │ │ ├── __init__.py │ │ ├── model_factories.py │ │ ├── test_admin_views.py │ │ ├── test_ajax_views.py │ │ ├── test_commands.py │ │ ├── test_managers.py │ │ ├── test_models.py │ │ ├── test_serializers.py │ │ └── test_templatetags.py │ └── views.py └── version.py ├── local_settings.example ├── __init__.py ├── settings.py └── wsgi.py ├── manage.py ├── package.json ├── requirements-dev.txt ├── requirements.txt └── setup.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | geokey/*/tests/* 4 | geokey/*/migrations/* 5 | geokey/*/__init__.py 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # GeoKey 2 | assets 3 | local_settings 4 | 5 | # Python folders and files 6 | .cache 7 | *.pyc 8 | *.egg-info 9 | 10 | # Coverage reports 11 | htmlcov 12 | .coverage 13 | coverage.xml 14 | 15 | # npm modules 16 | node_modules 17 | 18 | # Folder view configuration files 19 | .DS_Store 20 | Desktop.ini 21 | 22 | # Thumbnail cache files 23 | ._* 24 | Thumbs.db 25 | 26 | # Files that might appear on external disks 27 | .Spotlight-V100 28 | .Trashes 29 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent": 4, 3 | "browser": true, 4 | "globals": { 5 | "console": false, 6 | "L": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: python 3 | 4 | services: 5 | - postgresql 6 | 7 | python: 8 | - '2.7_with_system_site_packages' 9 | - '3.6' 10 | 11 | addons: 12 | postgresql: '9.4' 13 | apt: 14 | packages: 15 | - postgresql-9.4-postgis-2.3 16 | 17 | env: 18 | - DJANGO='>=1.8.19,<1.12' 19 | 20 | install: 21 | - sudo -E apt-get -yq update &>> ~/apt-get-update.log 22 | - sudo apt-get install binutils libav-tools 23 | - sudo apt-get -yq install libgdal-dev 24 | - gdal-config --version 25 | - export C_INCLUDE_PATH=/usr/include/gdal 26 | - export CPLUS_INCLUDE_PATH=/usr/include/gdal 27 | 28 | before_script: 29 | - psql template1 postgres -c "CREATE EXTENSION hstore;" 30 | - psql -c "CREATE USER django WITH PASSWORD 'django123';" -U postgres 31 | - psql -c "ALTER ROLE django WITH superuser;" -U postgres 32 | - psql -c "CREATE DATABASE geokey OWNER django;" -U postgres 33 | - psql -d geokey -c "CREATE EXTENSION postgis;" -U postgres 34 | - cp -r local_settings.example local_settings 35 | - pip install --upgrade pip 36 | - pip install coveralls 37 | - pip install -r requirements-dev.txt 38 | - pip install -e . 39 | - pip install django$DJANGO 40 | - python -c "import django; print('DJANGO %s' % django.get_version())" 41 | - python -c "from geokey.version import get_version; print('GEOKEY %s' % get_version())" 42 | - python manage.py migrate 43 | 44 | script: 45 | - coverage run --source=geokey.core,geokey.applications,geokey.extensions,geokey.users,geokey.superusertools,geokey.projects,geokey.categories,geokey.contributions,geokey.subsets,geokey.socialinteractions manage.py test 46 | 47 | after_success: 48 | - coveralls 49 | 50 | deploy: 51 | provider: pypi 52 | user: excites 53 | password: 54 | secure: EPsnf69HqWA8nT9ncgVuyhIJGZnR3Nrg8NUEzG4t1B1CTJfmwODC0Fb8Hybq25/0y6Fq3mBWE482xhscVHYvNh/7UnehU+y2riIj5iP+VYrfEuLzBN6ZkjYXOezafq6pzotwsj6JCDWKGmBE6Jy++FDsOFSzLK/R2p3HqnrnRpc= 55 | on: 56 | tags: true 57 | python: '3.6' 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y \ 5 | binutils \ 6 | g++ \ 7 | libav-tools \ 8 | python \ 9 | libpython-dev \ 10 | libgdal-dev \ 11 | imagemagick \ 12 | libmagickcore-dev \ 13 | libmagickwand-dev \ 14 | curl \ 15 | wget && \ 16 | apt-get clean 17 | 18 | RUN sed -i 's/\(\)/\1"read|write"\2/g' /etc/ImageMagick/policy.xml 19 | 20 | RUN python --version 21 | RUN curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" && \ 22 | python get-pip.py && \ 23 | rm get-pip.py && \ 24 | pip --version 25 | 26 | RUN gdal-config --version && \ 27 | export C_INCLUDE_PATH=/usr/include/gdal && \ 28 | export CPLUS_INCLUDE_PATH=/usr/include/gdal && \ 29 | pip install gdal==1.10 30 | 31 | RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \ 32 | apt-get install -y nodejs && \ 33 | node -v && npm -v 34 | 35 | ENV DOCKERIZE_VERSION v0.6.1 36 | RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && \ 37 | tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && \ 38 | rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz 39 | 40 | ADD /geokey /app 41 | 42 | WORKDIR /app 43 | RUN \ 44 | pip install --upgrade pip && \ 45 | pip install -r requirements.txt && \ 46 | pip install -r requirements-dev.txt && \ 47 | pip install -e /app 48 | RUN npm install 49 | -------------------------------------------------------------------------------- /GeoKey_updated_documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/GeoKey_updated_documentation.pdf -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | 5 | handlebars: { 6 | options: { 7 | namespace: 'Templates', 8 | processName: function(filePath) { 9 | var file = filePath.split('/')[3]; 10 | return file.substring(0, file.indexOf('.hbs')); 11 | }, 12 | processPartialName: function(filePath) { 13 | var file = filePath.split('/')[3]; 14 | return file.substring(1, file.indexOf('.hbs')); 15 | } 16 | }, 17 | 18 | compile: { 19 | files: { 20 | 'geokey/templates/templates.js': 'geokey/templates/handlebars/**/*.hbs' 21 | } 22 | } 23 | }, 24 | 25 | concat: { 26 | options: { 27 | separator: ';' 28 | }, 29 | 30 | handlebars: { 31 | src: ['geokey/templates/handlebars/helpers.js', 'geokey/templates/templates.js'], 32 | dest: 'geokey/static/js/templates.js' 33 | } 34 | }, 35 | 36 | uglify: { 37 | handlebars: { 38 | src: ['geokey/static/js/templates.js'], 39 | dest: 'geokey/static/js/templates.js' 40 | } 41 | }, 42 | 43 | watch: { 44 | options: { 45 | livereload: true, 46 | }, 47 | 48 | templates: { 49 | files: ['geokey/templates/handlebars/helpers.js', 'geokey/templates/handlebars/**/*.hbs'], 50 | tasks: ['handlebars', 'concat', 'uglify'], 51 | options: { 52 | spawn: false, 53 | } 54 | } 55 | } 56 | }); 57 | 58 | grunt.loadNpmTasks('grunt-contrib-handlebars'); 59 | grunt.loadNpmTasks('grunt-contrib-concat'); 60 | grunt.loadNpmTasks('grunt-contrib-watch'); 61 | grunt.loadNpmTasks('grunt-contrib-uglify'); 62 | 63 | grunt.registerTask('default', ['handlebars', 'concat', 'uglify', 'watch']); 64 | }; 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Extreme Citizen Science research group 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include *.md 3 | include *.txt 4 | recursive-include geokey/static * 5 | recursive-include geokey/templates * 6 | recursive-exclude * *.pyc 7 | -------------------------------------------------------------------------------- /db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mdillon/postgis 2 | 3 | COPY ./initdb-hstore.sh /docker-entrypoint-initdb.d/hstore.sh 4 | -------------------------------------------------------------------------------- /db/initdb-hstore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | export PGUSER="$POSTGRES_USER" 6 | 7 | "${psql[@]}" --dbname="template1" <<-'EOSQL' 8 | CREATE EXTENSION IF NOT EXISTS hstore; 9 | EOSQL 10 | 11 | "${psql[@]}" --dbname="$POSTGRES_DB" <<-'EOSQL' 12 | CREATE EXTENSION IF NOT EXISTS hstore; 13 | EOSQL 14 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | geokey: 5 | build: 6 | context: ../ 7 | dockerfile: ./geokey/Dockerfile 8 | links: 9 | - db 10 | entrypoint: dockerize -wait tcp://db:5432 -timeout 20s 11 | command: tail -f /dev/null 12 | ports: 13 | - "9000:8000" 14 | environment: 15 | DJANGO_DATABASE_HOST: db 16 | volumes: 17 | - ./geokey:/app/geokey 18 | - ./local_settings:/app/local_settings 19 | - ./assets:/app/assets 20 | db: 21 | build: 22 | context: ./db 23 | dockerfile: ./Dockerfile 24 | environment: 25 | POSTGRES_USER: django 26 | POSTGRES_PASSWORD: django123 27 | POSTGRES_DB: geokey 28 | -------------------------------------------------------------------------------- /geokey/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/__init__.py -------------------------------------------------------------------------------- /geokey/applications/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/applications/__init__.py -------------------------------------------------------------------------------- /geokey/applications/base.py: -------------------------------------------------------------------------------- 1 | """Base for applications.""" 2 | 3 | from model_utils import Choices 4 | 5 | 6 | STATUS = Choices('active', 'deleted') 7 | -------------------------------------------------------------------------------- /geokey/applications/forms.py: -------------------------------------------------------------------------------- 1 | """Forms for applications.""" 2 | 3 | from django import forms 4 | 5 | from .models import Application 6 | from django.utils.html import strip_tags 7 | 8 | 9 | class AppCreateForm(forms.ModelForm): 10 | """ 11 | Validates the inputs against the model definition. 12 | Used in .views.AppCreateView 13 | """ 14 | class Meta: 15 | model = Application 16 | fields = ('name', 'description', 'download_url', 'redirect_uris', 17 | 'authorization_grant_type', 'skip_authorization') 18 | 19 | def clean(self): 20 | """ 21 | Overwrites ModelForm's clean method to strip HTML Tags from name and 22 | description 23 | 24 | Returns 25 | ------- 26 | dict 27 | Cleaned form data including HTML free name and description 28 | """ 29 | cleaned_data = super(AppCreateForm, self).clean() 30 | cleaned_data['name'] = strip_tags(cleaned_data['name']) 31 | cleaned_data['description'] = strip_tags(cleaned_data['description']) 32 | 33 | return cleaned_data 34 | -------------------------------------------------------------------------------- /geokey/applications/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.RunSQL('DROP TABLE IF EXISTS applications_application CASCADE;'), 16 | migrations.RunSQL("DELETE FROM django_migrations WHERE app = 'applications';") 17 | ] 18 | -------------------------------------------------------------------------------- /geokey/applications/migrations/0002_auto_20150112_1807.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0002_auto_20150112_1700'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='application', 17 | name='skip_authorization', 18 | field=models.BooleanField(default=False), 19 | preserve_default=True, 20 | ), 21 | migrations.AlterField( 22 | model_name='application', 23 | name='user', 24 | field=models.ForeignKey(related_name='applications_application', to=settings.AUTH_USER_MODEL), 25 | preserve_default=True, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /geokey/applications/migrations/0003_auto_20171024_1400.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0002_auto_20150112_1807'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='user', 18 | field=models.ForeignKey(related_name='applications_application', blank=True, to=settings.AUTH_USER_MODEL, null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /geokey/applications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/applications/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/applications/models.py: -------------------------------------------------------------------------------- 1 | """Models for applications.""" 2 | 3 | try: 4 | # Python 3 5 | from re import fullmatch 6 | except ImportError: 7 | # Python 2 8 | def fullmatch(regex, string, flags=0): 9 | from re import match 10 | return match('(?:' + regex + r')\Z', string, flags=flags) 11 | 12 | try: 13 | # Python 3 14 | from urllib.parse import urlparse, parse_qsl 15 | except ImportError: 16 | # Python 2 17 | from urlparse import urlparse, parse_qsl 18 | 19 | from django.db import models 20 | 21 | from oauth2_provider.models import AbstractApplication 22 | 23 | from .base import STATUS 24 | from .managers import ApplicationManager 25 | 26 | 27 | class Application(AbstractApplication): 28 | """ 29 | Represents an application, that is registered in order to interact with 30 | GeoKey platform. 31 | """ 32 | 33 | description = models.TextField(null=True, blank=True) 34 | created_at = models.DateTimeField(auto_now_add=True) 35 | status = models.CharField( 36 | choices=STATUS, 37 | default=STATUS.active, 38 | max_length=20 39 | ) 40 | download_url = models.URLField(blank=False) 41 | 42 | objects = ApplicationManager() 43 | 44 | def redirect_uri_allowed(self, uri): 45 | """ 46 | Check if redirect URI is allowed, it can now be a regular expression, 47 | e.g: `https://(.*).example.com` will match all subdomains. 48 | """ 49 | for allowed_uri in self.redirect_uris.split(): 50 | parsed_allowed_uri = urlparse(allowed_uri) 51 | parsed_uri = urlparse(uri) 52 | 53 | if all([ 54 | parsed_allowed_uri.scheme == parsed_uri.scheme, 55 | fullmatch(parsed_allowed_uri.netloc, parsed_uri.netloc), 56 | parsed_allowed_uri.path == parsed_uri.path, 57 | ]): 58 | aqs_set = set(parse_qsl(parsed_allowed_uri.query)) 59 | uqs_set = set(parse_qsl(parsed_uri.query)) 60 | 61 | if aqs_set.issubset(uqs_set): 62 | return True 63 | 64 | return False 65 | -------------------------------------------------------------------------------- /geokey/applications/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/applications/tests/__init__.py -------------------------------------------------------------------------------- /geokey/applications/tests/model_factories.py: -------------------------------------------------------------------------------- 1 | """Model factories used for tests of applications.""" 2 | 3 | import datetime 4 | import factory 5 | 6 | from geokey.users.tests.model_factories import UserFactory 7 | 8 | from ..models import Application 9 | 10 | 11 | class ApplicationFactory(factory.django.DjangoModelFactory): 12 | class Meta: 13 | model = Application 14 | 15 | name = factory.Sequence(lambda n: 'name_%d' % n) 16 | description = factory.LazyAttribute(lambda o: '%s description' % o.name) 17 | created_at = datetime.date(2014, 11, 11) 18 | user = factory.SubFactory(UserFactory) 19 | download_url = 'http://example.com' 20 | redirect_uris = ['http://example.com/app'] 21 | skip_authorization = False 22 | status = 'active' 23 | -------------------------------------------------------------------------------- /geokey/applications/tests/test_managers.py: -------------------------------------------------------------------------------- 1 | """Tests for managers of applications.""" 2 | 3 | from django.test import TestCase 4 | from django.core.exceptions import PermissionDenied 5 | 6 | from nose.tools import raises 7 | 8 | from geokey.projects.tests.model_factories import UserFactory 9 | 10 | from .model_factories import ApplicationFactory 11 | 12 | from ..models import Application 13 | 14 | 15 | class ApplicationManagerTest(TestCase): 16 | def setUp(self): 17 | self.user1 = UserFactory.create() 18 | self.user2 = UserFactory.create() 19 | self.app1 = ApplicationFactory(**{ 20 | 'user': self.user1 21 | }) 22 | self.app2 = ApplicationFactory(**{ 23 | 'user': self.user1 24 | }) 25 | self.app3 = ApplicationFactory(**{ 26 | 'user': self.user2 27 | }) 28 | self.deleted_app = ApplicationFactory(**{ 29 | 'user': self.user1, 30 | 'status': 'deleted' 31 | }) 32 | 33 | def test_get_apps_with_user1(self): 34 | apps = Application.objects.get_list(self.user1) 35 | self.assertEqual(len(apps), 2) 36 | self.assertNotIn(self.deleted_app, apps) 37 | self.assertNotIn(self.app3, apps) 38 | 39 | def test_get_apps_with_user2(self): 40 | apps = Application.objects.get_list(self.user2) 41 | self.assertEqual(len(apps), 1) 42 | self.assertNotIn(self.deleted_app, apps) 43 | self.assertNotIn(self.app1, apps) 44 | self.assertNotIn(self.app2, apps) 45 | 46 | def test_get_single_app_with_user1(self): 47 | app = Application.objects.as_owner(self.user1, self.app1.id) 48 | self.assertEqual(app, self.app1) 49 | 50 | @raises(PermissionDenied) 51 | def test_get_single_app_with_user2(self): 52 | Application.objects.as_owner(self.user2, self.app1.id) 53 | -------------------------------------------------------------------------------- /geokey/applications/tests/test_models.py: -------------------------------------------------------------------------------- 1 | """Tests for models of applications.""" 2 | 3 | from django.test import TestCase 4 | 5 | from nose.tools import raises 6 | 7 | from .model_factories import ApplicationFactory 8 | 9 | from ..models import Application 10 | 11 | 12 | class ApplicationModelTest(TestCase): 13 | @raises(Application.DoesNotExist) 14 | def test_delete_app(self): 15 | app = ApplicationFactory() 16 | app.delete() 17 | Application.objects.get(pk=app.id) 18 | -------------------------------------------------------------------------------- /geokey/categories/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/categories/__init__.py -------------------------------------------------------------------------------- /geokey/categories/base.py: -------------------------------------------------------------------------------- 1 | """Base for categories.""" 2 | 3 | from model_utils import Choices 4 | 5 | 6 | STATUS = Choices('active', 'inactive', 'deleted') 7 | DEFAULT_STATUS = Choices('active', 'pending') 8 | -------------------------------------------------------------------------------- /geokey/categories/forms.py: -------------------------------------------------------------------------------- 1 | """Forms for categories.""" 2 | 3 | from django import forms 4 | 5 | from .models import Category, Field 6 | 7 | 8 | class CategoryCreateForm(forms.ModelForm): 9 | """ 10 | Validates the inputs against the model definition. 11 | Used in .views.ObservationTypeAdminCreateView 12 | """ 13 | 14 | class Meta: 15 | model = Category 16 | fields = ('name', 'description', 'default_status') 17 | 18 | 19 | class FieldCreateForm(forms.ModelForm): 20 | """ 21 | Validates the inputs against the model definition. 22 | Used in .views.FieldAdminCreateView 23 | """ 24 | class Meta: 25 | model = Field 26 | fields = ('name', 'description', 'required') 27 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0002_auto_20150106_1338.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('categories', '0001_initial'), 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('projects', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AddField( 18 | model_name='category', 19 | name='creator', 20 | field=models.ForeignKey(to=settings.AUTH_USER_MODEL), 21 | preserve_default=True, 22 | ), 23 | migrations.AddField( 24 | model_name='category', 25 | name='project', 26 | field=models.ForeignKey(related_name='categories', to='projects.Project'), 27 | preserve_default=True, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0003_datefield.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0002_auto_20150106_1338'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='DateField', 16 | fields=[ 17 | ('field_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='categories.Field')), 18 | ], 19 | options={ 20 | }, 21 | bases=('categories.field',), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0004_timefield.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0003_datefield'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='TimeField', 16 | fields=[ 17 | ('field_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='categories.Field')), 18 | ], 19 | options={ 20 | }, 21 | bases=('categories.field',), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0005_auto_20150112_1731.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0004_timefield'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='category', 16 | options={'ordering': ['id']}, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0006_category_display_field.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0005_auto_20150112_1731'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='category', 16 | name='display_field', 17 | field=models.ForeignKey(related_name='display_field_of', default=None, to='categories.Field', null=True), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0007_auto_20150130_1155.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations 5 | 6 | 7 | def update_display_field(apps, schema_editor): 8 | Category = apps.get_model("categories", "Category") 9 | Field = apps.get_model("categories", "Field") 10 | for category in Category.objects.all(): 11 | try: 12 | first_field = category.fields.get(order=0) 13 | category.display_field = first_field 14 | category.save() 15 | except Field.DoesNotExist: 16 | pass 17 | 18 | 19 | class Migration(migrations.Migration): 20 | 21 | dependencies = [ 22 | ('categories', '0006_category_display_field'), 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(update_display_field), 27 | ] 28 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0008_auto_20150130_1216.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0007_auto_20150130_1155'), 11 | ] 12 | 13 | operations = [ 14 | # migrations.AlterField( 15 | # model_name='category', 16 | # name='display_field', 17 | # field=models.ForeignKey(related_name='display_field_of', to='categories.Field', null=True), 18 | # preserve_default=True, 19 | # ), 20 | ] 21 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0010_auto_20150202_1023.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0008_auto_20150130_1216'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='category', 16 | options={'ordering': ['name']}, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0011_auto_20150220_1413.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0010_auto_20150202_1023'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='textfield', 16 | name='maxlength', 17 | field=models.IntegerField(null=True, blank=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AddField( 21 | model_name='textfield', 22 | name='textarea', 23 | field=models.BooleanField(default=False), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0012_auto_20150223_1311.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0011_auto_20150220_1413'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterModelOptions( 15 | name='category', 16 | options={'ordering': ['order']}, 17 | ), 18 | migrations.AddField( 19 | model_name='category', 20 | name='order', 21 | field=models.IntegerField(default=0), 22 | preserve_default=True, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0013_auto_20150130_1440.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations 5 | from geokey.contributions.models import Observation 6 | 7 | 8 | def populate_display_field(apps, schema_editor): 9 | Category = apps.get_model("categories", "Category") 10 | # Observation = apps.get_model("contributions", "Observation") 11 | 12 | for category in Category.objects.all(): 13 | if category.display_field is not None: 14 | for obs in Observation.objects.filter(category=category): 15 | obs.update_display_field() 16 | obs.save() 17 | 18 | 19 | class Migration(migrations.Migration): 20 | 21 | dependencies = [ 22 | ('categories', '0012_auto_20150223_1311'), 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(populate_display_field), 27 | ] 28 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0014_auto_20160104_1409.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0013_auto_20150130_1440'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='category', 16 | name='symbol', 17 | field=models.ImageField(max_length=500, null=True, upload_to=b'symbols'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0015_lookupvalue_symbol.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0014_auto_20160104_1409'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='lookupvalue', 16 | name='symbol', 17 | field=models.ImageField(max_length=500, null=True, upload_to=b'symbols'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0016_multiplelookupvalue_symbol.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0015_lookupvalue_symbol'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='multiplelookupvalue', 16 | name='symbol', 17 | field=models.ImageField(max_length=500, null=True, upload_to=b'symbols'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0017_category_expiry_field.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('categories', '0016_multiplelookupvalue_symbol'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='category', 16 | name='expiry_field', 17 | field=models.ForeignKey(related_name='expiry_field_of', to='categories.Field', null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /geokey/categories/migrations/0019_auto_20181028_1638.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.16 on 2018-10-28 16:38 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('categories', '0018_historicalcategory'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='lookupvalue', 17 | options={'ordering': ['order']}, 18 | ), 19 | migrations.AlterModelOptions( 20 | name='multiplelookupvalue', 21 | options={'ordering': ['order']}, 22 | ), 23 | migrations.AddField( 24 | model_name='lookupvalue', 25 | name='order', 26 | field=models.IntegerField(default=0), 27 | ), 28 | migrations.AddField( 29 | model_name='multiplelookupvalue', 30 | name='order', 31 | field=models.IntegerField(default=0), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /geokey/categories/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/categories/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/categories/mixins.py: -------------------------------------------------------------------------------- 1 | """Mixins for categories.""" 2 | 3 | from geokey.core.decorators import handle_exceptions_for_admin 4 | from geokey.categories.models import Category, Field 5 | 6 | 7 | class CategoryMixin(object): 8 | """A mixin for category.""" 9 | 10 | @handle_exceptions_for_admin 11 | def get_context_data(self, project_id, category_id, *args, **kwargs): 12 | """ 13 | Return the context to render the view. 14 | 15 | Overwrite the method to add the project and the category to the 16 | context. 17 | 18 | Returns 19 | ------- 20 | dict 21 | Context. 22 | """ 23 | category = Category.objects.as_admin( 24 | self.request.user, 25 | project_id, 26 | category_id 27 | ) 28 | return super(CategoryMixin, self).get_context_data( 29 | project=category.project, 30 | category=category, 31 | *args, 32 | **kwargs 33 | ) 34 | 35 | 36 | class FieldMixin(object): 37 | """A mixin for field.""" 38 | 39 | @handle_exceptions_for_admin 40 | def get_context_data(self, project_id, category_id, field_id, 41 | *args, **kwargs): 42 | """ 43 | Return the context to render the view. 44 | 45 | Overwrite the method to add the project, the category and the field to 46 | the context. 47 | 48 | Returns 49 | ------- 50 | dict 51 | Context. 52 | """ 53 | field = Field.objects.as_admin( 54 | self.request.user, 55 | project_id, 56 | category_id, 57 | field_id 58 | ) 59 | return super(FieldMixin, self).get_context_data( 60 | project=field.category.project, 61 | category=field.category, 62 | field=field, 63 | is_display_field=(field == field.category.display_field), 64 | is_expiry_field=(field == field.category.expiry_field), 65 | *args, 66 | **kwargs 67 | ) 68 | -------------------------------------------------------------------------------- /geokey/categories/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/categories/templatetags/__init__.py -------------------------------------------------------------------------------- /geokey/categories/templatetags/filter_fields.py: -------------------------------------------------------------------------------- 1 | """Template tags for filtering fields.""" 2 | 3 | from django import template 4 | 5 | 6 | register = template.Library() 7 | 8 | 9 | @register.filter 10 | def only_fields(fields, type_names): 11 | type_names = [type_name.strip() for type_name in type_names.split(',')] 12 | return [field for field in fields if field.type_name in type_names] 13 | 14 | 15 | @register.filter 16 | def except_fields(fields, type_names): 17 | type_names = [type_name.strip() for type_name in type_names.split(',')] 18 | return [field for field in fields if field.type_name not in type_names] 19 | -------------------------------------------------------------------------------- /geokey/categories/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/categories/tests/__init__.py -------------------------------------------------------------------------------- /geokey/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def allowed_contributors(request): 5 | """Allows access to settings constants within templates.""" 6 | _ = request # PEP-8 compliance without reducing readability. 7 | default = ("true", "auth", "false") 8 | # return the value you want as a dictionary. you may add multiple values in there. 9 | if hasattr(settings, 'ALLOWED_CONTRIBUTORS'): 10 | return {'ALLOWED_CONTRIBUTORS': settings.ALLOWED_CONTRIBUTORS} 11 | else: 12 | return {'ALLOWED_CONTRIBUTORS': default} 13 | -------------------------------------------------------------------------------- /geokey/contributions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/base.py: -------------------------------------------------------------------------------- 1 | """Base for contributions.""" 2 | 3 | from model_utils import Choices 4 | 5 | 6 | LOCATION_STATUS = Choices('active', 'review') 7 | OBSERVATION_STATUS = Choices('active', 'draft', 'review', 'pending', 'deleted') 8 | COMMENT_STATUS = Choices('active', 'deleted') 9 | COMMENT_REVIEW = Choices('open', 'resolved') 10 | MEDIA_STATUS = Choices('active', 'deleted') 11 | ACCEPTED_AUDIO_TYPES = ( 12 | ('MPEG ADTS, layer III', 'mp3'), 13 | ('Audio file', 'mp3'), 14 | ('Ogg data, Opus audio', 'opus'), 15 | ('Ogg data', 'ogg'), 16 | ('WAVE audio', 'wav'), 17 | ('AAC-LC (.M4A) Audio', 'm4a'), 18 | ('MPEG v4 system', 'm4a'), 19 | ('Adaptive Multi-Rate Codec', 'amr'), 20 | ('MPEG v4 system, 3GPP', '3gp'), 21 | ('AIFF audio', 'aiff'), 22 | ('AAC', 'aac'), 23 | ('FLAC audio bitstream data', 'flac'), 24 | ('Microsoft ASF', 'wma'), 25 | ) 26 | ACCEPTED_VIDEO_TYPES = ( 27 | ('Apple QuickTime movie', 'mov'), 28 | ('RIFF (little-endian) data, AVI', 'avi'), 29 | ('Macromedia Flash Video', 'flv'), 30 | ('Matroska data', 'mkv'), 31 | ('MPEG sequence', 'mpg'), 32 | ('Macromedia Flash data', 'swf'), 33 | ('WebM', 'webm'), 34 | ('Microsoft ASF', 'wmv'), 35 | ) 36 | ACCEPTED_IMAGE_TYPES = ( 37 | ('GIF image data', 'gif'), 38 | ('JPEG image data', 'jpg'), 39 | ('PNG image data', 'png'), 40 | ('Scalable Vector Graphics', 'svg'), 41 | ('TIFF image data', 'tiff'), 42 | ) 43 | ACCEPTED_DOC_TYPES = ( 44 | ('PDF document', 'pdf'), 45 | ) 46 | ACCEPTED_FILE_TYPES = \ 47 | ACCEPTED_AUDIO_TYPES + \ 48 | ACCEPTED_VIDEO_TYPES + \ 49 | ACCEPTED_IMAGE_TYPES + \ 50 | ACCEPTED_DOC_TYPES 51 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0003_auto_20150121_1544.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contributions', '0002_auto_20150106_1338'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='historicalobservation', 16 | name='display_field', 17 | field=models.TextField(null=True, blank=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AddField( 21 | model_name='observation', 22 | name='display_field', 23 | field=models.TextField(null=True, blank=True), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0004_auto_20150121_1455.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | 6 | 7 | def populate_display_field(apps, schema_editor): 8 | Observation = apps.get_model("contributions", "Observation") 9 | for observation in Observation.objects.all(): 10 | first_field = observation.category.fields.get(order=0) 11 | value = observation.attributes.get(first_field.key) 12 | observation.display_field = '%s:%s' % (first_field.key, value) 13 | observation.save() 14 | 15 | 16 | class Migration(migrations.Migration): 17 | 18 | dependencies = [ 19 | ('contributions', '0003_auto_20150121_1544'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(populate_display_field), 24 | ] 25 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0005_auto_20150202_1135.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | 6 | 7 | def update_search_matches(apps, schema_editor): 8 | Observation = apps.get_model("contributions", "Observation") 9 | for observation in Observation.objects.all(): 10 | observation.update_search_matches() 11 | observation.save() 12 | 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [ 17 | ('contributions', '0004_auto_20150121_1455'), 18 | ] 19 | 20 | operations = [ 21 | migrations.RunPython(update_search_matches), 22 | ] 23 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0006_auto_20150312_1247.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | try: 6 | from django.contrib.postgres.fields import JSONField 7 | except ImportError: 8 | from django_pgjson.fields import JsonBField as JSONField 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | ('contributions', '0005_auto_20150202_1135'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='historicalobservation', 20 | name='properties', 21 | field=JSONField(default={}), 22 | preserve_default=True, 23 | ), 24 | migrations.AddField( 25 | model_name='observation', 26 | name='properties', 27 | field=JSONField(default={}), 28 | preserve_default=True, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0007_auto_20150312_1249.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | 6 | 7 | def convert_value(val): 8 | if val is not None and isinstance(val, str): 9 | try: # it's an int 10 | return int(val) 11 | except ValueError: 12 | pass 13 | 14 | try: # it's a float 15 | return float(val) 16 | except ValueError: 17 | pass 18 | 19 | # cannot convert to number, returns string or None 20 | return val 21 | 22 | 23 | def copy_attributes(apps, schema_editor): 24 | Observation = apps.get_model("contributions", "Observation") 25 | for observation in Observation.objects.all(): 26 | properties = {} 27 | for field in observation.category.fields.all(): 28 | value = observation.attributes.get(field.key) 29 | if value is not None: 30 | properties[field.key] = convert_value(value) 31 | 32 | observation.properties = properties 33 | observation.save() 34 | 35 | 36 | class Migration(migrations.Migration): 37 | 38 | dependencies = [ 39 | ('contributions', '0006_auto_20150312_1247'), 40 | ] 41 | 42 | operations = [ 43 | migrations.RunPython(copy_attributes), 44 | ] 45 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0008_auto_20150312_1508.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contributions', '0007_auto_20150312_1249'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='historicalobservation', 16 | name='attributes', 17 | ), 18 | migrations.RemoveField( 19 | model_name='observation', 20 | name='attributes', 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0009_auto_20150420_1549.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | import django.db.models.deletion 6 | from django.conf import settings 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('contributions', '0008_auto_20150312_1508'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterModelOptions( 17 | name='historicalobservation', 18 | options={'ordering': ('-history_date', '-history_id'), 'get_latest_by': 'history_date', 'verbose_name': 'historical observation'}, 19 | ), 20 | migrations.AlterField( 21 | model_name='historicalobservation', 22 | name='history_user', 23 | field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0011_auto_20150527_1255.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | def clean_youtube_links(apps, schema_editor): 8 | VideoFile = apps.get_model("contributions", "VideoFile") 9 | 10 | for file in VideoFile.objects.all(): 11 | new_link = file.youtube_link.replace('http://', 'https://') 12 | file.youtube_link = new_link 13 | file.save() 14 | 15 | 16 | class Migration(migrations.Migration): 17 | 18 | dependencies = [ 19 | ('contributions', '0010_auto_20150511_1132'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(clean_youtube_links), 24 | ] 25 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0012_auto_20150807_0854.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | def update_count(apps, schema_editor): 8 | Observation = apps.get_model('contributions', 'Observation') 9 | 10 | for o in Observation.objects.all(): 11 | o.num_media = o.files_attached.exclude(status='deleted').count() 12 | o.num_comments = o.comments.exclude(status='deleted').count() 13 | o.save() 14 | 15 | 16 | class Migration(migrations.Migration): 17 | 18 | dependencies = [ 19 | ('contributions', '0011_auto_20150527_1255'), 20 | ] 21 | 22 | operations = [ 23 | migrations.AddField( 24 | model_name='historicalobservation', 25 | name='num_comments', 26 | field=models.IntegerField(default=0), 27 | preserve_default=True, 28 | ), 29 | migrations.AddField( 30 | model_name='historicalobservation', 31 | name='num_media', 32 | field=models.IntegerField(default=0), 33 | preserve_default=True, 34 | ), 35 | migrations.AddField( 36 | model_name='observation', 37 | name='num_comments', 38 | field=models.IntegerField(default=0), 39 | preserve_default=True, 40 | ), 41 | migrations.AddField( 42 | model_name='observation', 43 | name='num_media', 44 | field=models.IntegerField(default=0), 45 | preserve_default=True, 46 | ), 47 | migrations.RunPython(update_count) 48 | ] 49 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0014_auto_20150907_1345.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | import re 6 | 7 | 8 | def create_search_index(apps, schema_editor): 9 | Observation = apps.get_model('contributions', 'Observation') 10 | 11 | for o in Observation.objects.all(): 12 | search_index = [] 13 | 14 | fields = o.search_matches.split('#####') 15 | for field in fields: 16 | if field: 17 | value = field.split(':')[1] 18 | 19 | cleaned = re.sub(r'[\W_]+', ' ', value) 20 | terms = cleaned.lower().split() 21 | 22 | search_index = search_index + list( 23 | set(terms) - set(search_index) 24 | ) 25 | 26 | o.search_index = ','.join(search_index) 27 | o.save() 28 | 29 | 30 | class Migration(migrations.Migration): 31 | 32 | dependencies = [ 33 | ('contributions', '0013_auto_20150907_1345'), 34 | ] 35 | 36 | operations = [ 37 | migrations.RunPython(create_search_index) 38 | ] 39 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0015_auto_20150907_1345.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contributions', '0014_auto_20150907_1345'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='historicalobservation', 16 | name='search_matches', 17 | ), 18 | migrations.RemoveField( 19 | model_name='observation', 20 | name='search_matches', 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0016_audiofile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contributions', '0015_auto_20150907_1345'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='AudioFile', 16 | fields=[ 17 | ('mediafile_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='contributions.MediaFile')), 18 | ('audio', models.FileField(upload_to=b'user-uploads/audio')), 19 | ], 20 | options={ 21 | 'ordering': ['id'], 22 | }, 23 | bases=('contributions.mediafile',), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0017_auto_20160531_1434.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contributions', '0016_audiofile'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='historicalobservation', 16 | name='expiry_field', 17 | field=models.DateTimeField(null=True, blank=True), 18 | ), 19 | migrations.AddField( 20 | model_name='observation', 21 | name='expiry_field', 22 | field=models.DateTimeField(null=True, blank=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0019_auto_20181020_1915.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('contributions', '0018_historicalcomment'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='DocumentFile', 16 | fields=[ 17 | ('mediafile_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='contributions.MediaFile')), 18 | ('document', models.FileField(upload_to=b'user-uploads/documents')), 19 | ('thumbnail', models.ImageField(null=True, upload_to=b'user-uploads/documents')), 20 | ], 21 | options={ 22 | 'ordering': ['id'], 23 | }, 24 | bases=('contributions.mediafile',), 25 | ), 26 | migrations.AlterModelOptions( 27 | name='mediafile', 28 | options={}, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/0020_update_media_and_comments_count.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.db import migrations, transaction, IntegrityError 4 | 5 | 6 | def update_media_and_comments_count(apps, schema_editor): 7 | Observation = apps.get_model('contributions', 'Observation') 8 | 9 | for observation in Observation.objects.all(): 10 | try: 11 | with transaction.atomic(): 12 | observation.num_media = observation.files_attached.count() 13 | observation.num_comments = observation.comments.count() 14 | observation.save() 15 | except IntegrityError: 16 | pass 17 | 18 | 19 | class Migration(migrations.Migration): 20 | 21 | dependencies = [ 22 | ('contributions', '0019_auto_20181020_1915'), 23 | ] 24 | 25 | operations = [ 26 | migrations.RunPython(update_media_and_comments_count) 27 | ] 28 | -------------------------------------------------------------------------------- /geokey/contributions/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/parsers/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/parsers/geojson.py: -------------------------------------------------------------------------------- 1 | """GeoJSON parser.""" 2 | 3 | import json 4 | from rest_framework.parsers import BaseParser 5 | 6 | 7 | class GeoJsonParser(BaseParser): 8 | """Parses GeoJson into deserialisable native Python objects """ 9 | media_type = 'application/json' 10 | 11 | def parse(self, stream, media_type=None, parser_context=None): 12 | """ 13 | Parses the GeoJson imput and returns deserialisable Python native 14 | """ 15 | parser_context = parser_context or {} 16 | 17 | request_data = stream.read() 18 | data = json.loads(request_data) 19 | 20 | if 'geometry' in data: 21 | if 'location' not in data: 22 | data['location'] = {} 23 | 24 | data['location']['geometry'] = json.dumps(data.pop('geometry')) 25 | 26 | return data 27 | -------------------------------------------------------------------------------- /geokey/contributions/renderers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/renderers/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/renderers/geojson.py: -------------------------------------------------------------------------------- 1 | """GeoJSON renderer.""" 2 | 3 | import json 4 | 5 | from rest_framework.renderers import BaseRenderer 6 | 7 | 8 | class GeoJsonRenderer(BaseRenderer): 9 | """ 10 | Renderes serialised Contributions into GeoJson 11 | """ 12 | media_type = 'application/json' 13 | format = 'json' 14 | separators = (',', ':') 15 | 16 | def render_single(self, data): 17 | """ 18 | Renders a single `Contribution` into GeoJson 19 | """ 20 | try: 21 | data['type'] = "Feature" 22 | data['geometry'] = json.loads(data.get('location').pop('geometry')) 23 | return data 24 | except: 25 | return data 26 | 27 | def render_many(self, data): 28 | """ 29 | Creates a `FeatureCollection` object and adds Contributions to 30 | `features`. 31 | """ 32 | return { 33 | "type": "FeatureCollection", 34 | "features": [self.render_single(item) for item in data] 35 | } 36 | 37 | def render(self, data, accepted_media_type=None, renderer_context=None): 38 | """ 39 | Renders `data` into serialized GeoJson. 40 | """ 41 | 42 | if '(e.g:bbox=xmin,ymin,xmax,ymax)' in str(data): 43 | rendered = {'error': str(data)} 44 | return json.dumps(rendered) 45 | if data is None: 46 | return '' 47 | 48 | if 'error' in data: 49 | rendered = data 50 | elif isinstance(data, dict): 51 | rendered = self.render_single(data) 52 | else: 53 | rendered = self.render_many(data) 54 | 55 | return json.dumps(rendered, separators=self.separators) 56 | -------------------------------------------------------------------------------- /geokey/contributions/renderers/kml.py: -------------------------------------------------------------------------------- 1 | """KML renderer.""" 2 | 3 | from django.template.loader import render_to_string 4 | 5 | from rest_framework.renderers import BaseRenderer 6 | 7 | 8 | class KmlRenderer(BaseRenderer): 9 | media_type = 'application/vnd.google-earth.kml+xml' 10 | format = 'kml' 11 | 12 | def render(self, data, accepted_media_type=None, renderer_context=None): 13 | rendered = render_to_string( 14 | 'geometries/placemarks.kml', 15 | {'data': data} 16 | ) 17 | 18 | return rendered 19 | -------------------------------------------------------------------------------- /geokey/contributions/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/templatetags/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/tests/comments/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/comments/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/tests/comments/test_managers.py: -------------------------------------------------------------------------------- 1 | """Tests for managers of contributions (comments).""" 2 | 3 | from django.test import TestCase 4 | 5 | from ..model_factories import ObservationFactory, CommentFactory 6 | from geokey.contributions.models import Comment 7 | 8 | 9 | class CommentTest(TestCase): 10 | def test_get_comments(self): 11 | observation = ObservationFactory.create() 12 | CommentFactory.create_batch(5, **{'commentto': observation}) 13 | CommentFactory.create(**{ 14 | 'commentto': observation, 15 | 'status': 'deleted' 16 | }) 17 | comments = Comment.objects.all() 18 | self.assertEqual(len(comments), 5) 19 | for comment in comments: 20 | self.assertNotEqual(comment.status, 'deleted') 21 | -------------------------------------------------------------------------------- /geokey/contributions/tests/comments/test_models.py: -------------------------------------------------------------------------------- 1 | """Tests for models of contributions (comments).""" 2 | 3 | from django.test import TestCase 4 | 5 | from nose.tools import raises 6 | 7 | from geokey.contributions.models import Comment, post_save_count_update 8 | from ..model_factories import ObservationFactory, CommentFactory 9 | 10 | 11 | class TestCommentPostSave(TestCase): 12 | def test_post_save_comment_count_update(self): 13 | observation = ObservationFactory() 14 | CommentFactory.create_batch(5, **{'commentto': observation}) 15 | comment = CommentFactory.create(**{ 16 | 'commentto': observation, 17 | 'status': 'deleted' 18 | }) 19 | 20 | post_save_count_update( 21 | Comment, 22 | instance=comment, 23 | created=True) 24 | 25 | observation.refresh_from_db() 26 | self.assertEqual(observation.num_media, 0) 27 | self.assertEqual(observation.num_comments, 5) 28 | 29 | 30 | class CommentTest(TestCase): 31 | @raises(Comment.DoesNotExist) 32 | def test_delete_comment(self): 33 | comment = CommentFactory() 34 | comment.delete() 35 | Comment.objects.get(pk=comment.id) 36 | 37 | def test_delete_nested(self): 38 | comment = CommentFactory() 39 | response = CommentFactory(**{'respondsto': comment}) 40 | CommentFactory.create_batch(3, **{'respondsto': response}) 41 | self.assertEqual(len(Comment.objects.all()), 5) 42 | comment.delete() 43 | self.assertEqual(len(Comment.objects.all()), 0) 44 | -------------------------------------------------------------------------------- /geokey/contributions/tests/locations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/locations/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/tests/media/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_1.mp3 -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_10.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_10.flac -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_12.wma: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_12.wma -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_2.3gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_2.3gp -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_3.opus: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_3.opus -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_4.m4a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_4.m4a -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_5.amr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_5.amr -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_6.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_6.aiff -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_7.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_7.wav -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_8 -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_8.opus: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_8.opus -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/audio_9.aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/audio_9.aac -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/document_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/document_1.pdf -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/document_2.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/document_2.doc -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/image_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/image_01.png -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/image_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/image_02.jpg -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/image_03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/image_03.gif -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/image_05.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/image_05.tiff -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/text_1.txt: -------------------------------------------------------------------------------- 1 | A test text file. 2 | -------------------------------------------------------------------------------- /geokey/contributions/tests/media/files/video.MOV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/files/video.MOV -------------------------------------------------------------------------------- /geokey/contributions/tests/media/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/media/helpers/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/tests/media/helpers/document_helpers.py: -------------------------------------------------------------------------------- 1 | """Document helpers of contributions.""" 2 | 3 | from os.path import dirname, normpath, abspath, join 4 | 5 | from django.core.files import File 6 | from django.core.files.base import ContentFile 7 | 8 | 9 | def get_pdf_document(file_name='document_1.pdf'): 10 | pdf_file = File(open( 11 | normpath(join( 12 | dirname(dirname(abspath(__file__))), 13 | 'files/document_1.pdf' 14 | )), 15 | 'rb' 16 | )) 17 | 18 | the_file = ContentFile(pdf_file.read(), file_name) 19 | the_file.content_type = 'application/pdf' 20 | 21 | return the_file 22 | 23 | 24 | def get_doc_document(file_name='document_2.doc'): 25 | doc_file = File(open( 26 | normpath(join( 27 | dirname(dirname(abspath(__file__))), 28 | 'files/document_2.doc' 29 | )), 30 | 'rb' 31 | )) 32 | 33 | the_file = ContentFile(doc_file.read(), file_name) 34 | the_file.content_type = 'application/msword' 35 | 36 | return the_file 37 | -------------------------------------------------------------------------------- /geokey/contributions/tests/model_factories.py: -------------------------------------------------------------------------------- 1 | """Model factories used for tests of contributions.""" 2 | 3 | import datetime 4 | import factory 5 | 6 | from geokey.users.tests.model_factories import UserFactory 7 | from geokey.projects.tests.model_factories import ProjectFactory 8 | from geokey.categories.tests.model_factories import CategoryFactory 9 | 10 | from ..models import Location, Observation, Comment 11 | 12 | 13 | class LocationFactory(factory.django.DjangoModelFactory): 14 | class Meta: 15 | model = Location 16 | 17 | name = factory.Sequence(lambda n: 'name_%d' % n) 18 | description = factory.LazyAttribute(lambda o: '%s description' % o.name) 19 | geometry = 'POINT(-0.134040713310241 51.52447878755655)' 20 | created_at = datetime.date(2014, 11, 11) 21 | creator = factory.SubFactory(UserFactory) 22 | status = 'active' 23 | version = 1 24 | private = False 25 | private_for_project = None 26 | 27 | 28 | class ObservationFactory(factory.django.DjangoModelFactory): 29 | class Meta: 30 | model = Observation 31 | 32 | location = factory.SubFactory(LocationFactory) 33 | project = factory.SubFactory(ProjectFactory) 34 | status = 'active' 35 | category = factory.SubFactory(CategoryFactory) 36 | created_at = datetime.date(2014, 11, 11) 37 | creator = factory.SubFactory(UserFactory) 38 | version = 1 39 | 40 | 41 | class CommentFactory(factory.django.DjangoModelFactory): 42 | class Meta: 43 | model = Comment 44 | 45 | text = factory.Sequence(lambda n: 'Comment number %d' % n) 46 | created_at = datetime.date(2014, 11, 11) 47 | creator = factory.SubFactory(UserFactory) 48 | commentto = factory.SubFactory(ObservationFactory) 49 | respondsto = None 50 | status = 'active' 51 | -------------------------------------------------------------------------------- /geokey/contributions/tests/observations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/contributions/tests/observations/__init__.py -------------------------------------------------------------------------------- /geokey/contributions/views/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /geokey/contributions/views/base.py: -------------------------------------------------------------------------------- 1 | """Base for views of contributions.""" 2 | 3 | from geokey.projects.models import Project 4 | 5 | 6 | class SingleAllContribution(object): 7 | """Base class for a single contribution of all contributions.""" 8 | 9 | def get_contribution(self, user, project_id, contribution_id): 10 | """ 11 | Get a single contribution. 12 | 13 | Parameters 14 | ---------- 15 | user : geokey.users.models.User 16 | User requesting the request. 17 | project_id : int 18 | Identifies the project in the database. 19 | contribution_id : int 20 | Identifies the contribution in the database. 21 | 22 | Returns 23 | ------- 24 | geokey.contributions.models.Observation 25 | Contribution with the required ID. 26 | """ 27 | project = Project.objects.get_single(user, project_id) 28 | 29 | if project.can_moderate(user): 30 | return project\ 31 | .get_all_contributions(user)\ 32 | .for_moderator(user)\ 33 | .select_related('location', 'project')\ 34 | .prefetch_related('comments')\ 35 | .get(pk=contribution_id) 36 | else: 37 | return project\ 38 | .get_all_contributions(user)\ 39 | .for_viewer(user)\ 40 | .select_related('location', 'project')\ 41 | .prefetch_related('comments')\ 42 | .get(pk=contribution_id) 43 | -------------------------------------------------------------------------------- /geokey/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/__init__.py -------------------------------------------------------------------------------- /geokey/core/adapters.py: -------------------------------------------------------------------------------- 1 | """Core adapters.""" 2 | 3 | import re 4 | 5 | from django.core.urlresolvers import reverse 6 | from django.contrib import messages 7 | 8 | from allauth.account.adapter import DefaultAccountAdapter 9 | from allauth.socialaccount.adapter import DefaultSocialAccountAdapter 10 | from allauth.account.models import EmailAddress 11 | 12 | 13 | class AccountAdapter(DefaultAccountAdapter): 14 | """Adapter for accounts.""" 15 | 16 | username_regex = re.compile(r'^.+$') 17 | 18 | def respond_user_inactive(self, request, user): 19 | """Resend email confirmation instructions if user is inactive.""" 20 | try: 21 | email_address = EmailAddress.objects.get( 22 | user=user, 23 | email=user.email) 24 | self.add_message( 25 | request, 26 | messages.INFO, 27 | 'account/messages/' 28 | 'email_confirmation_sent.txt', 29 | {'email': user.email}) 30 | email_address.send_confirmation(request) 31 | except EmailAddress.DoesNotExist: 32 | pass 33 | 34 | return super(AccountAdapter, self).respond_user_inactive(request, user) 35 | 36 | 37 | class SocialAccountAdapter(DefaultSocialAccountAdapter): 38 | """Adapter for social accounts.""" 39 | 40 | def get_connect_redirect_url(self, request, socialaccount): 41 | """Return URL after successfull connecting a social account.""" 42 | assert request.user.is_authenticated() 43 | url = reverse('admin:userprofile') 44 | return url 45 | -------------------------------------------------------------------------------- /geokey/core/base.py: -------------------------------------------------------------------------------- 1 | """Core base.""" 2 | 3 | from model_utils import Choices 4 | 5 | 6 | STATUS_ACTION = Choices('created', 'updated', 'deleted') 7 | LOG_MODELS = { 8 | 'Project': [ 9 | 'name', 10 | 'status', 11 | 'isprivate', 12 | 'islocked', 13 | 'everyone_contributes', 14 | 'geographic_extent', 15 | ], 16 | 'Admins': [], 17 | 'UserGroup': [ 18 | 'name', 19 | 'can_contribute', 20 | 'can_moderate', 21 | ], 22 | 'Category': [ 23 | 'name', 24 | 'status', 25 | 'default_status', 26 | ], 27 | 'TextField': [ 28 | 'name', 29 | 'status', 30 | 'required', 31 | ], 32 | 'NumericField': [ 33 | 'name', 34 | 'status', 35 | 'required', 36 | ], 37 | 'DateTimeField': [ 38 | 'name', 39 | 'status', 40 | 'required', 41 | ], 42 | 'DateField': [ 43 | 'name', 44 | 'status', 45 | 'required', 46 | ], 47 | 'TimeField': [ 48 | 'name', 49 | 'status', 50 | 'required', 51 | ], 52 | 'LookupField': [ 53 | 'name', 54 | 'status', 55 | 'required', 56 | ], 57 | 'MultipleLookupField': [ 58 | 'name', 59 | 'status', 60 | 'required', 61 | ], 62 | 'Location': [ 63 | 'name', 64 | 'geometry', 65 | ], 66 | 'Observation': [ 67 | 'status', 68 | 'properties', 69 | ], 70 | 'Comment': [ 71 | 'status', 72 | ], 73 | 'ImageFile': [ 74 | 'status', 75 | ], 76 | 'DocumentFile': [ 77 | 'status', 78 | ], 79 | 'VideoFile': [ 80 | 'status', 81 | ], 82 | 'AudioFile': [ 83 | 'status', 84 | ], 85 | 'Subset': [ 86 | 'name', 87 | ], 88 | } 89 | LOG_M2M_RELATIONS = ['UserGroup_users'] 90 | -------------------------------------------------------------------------------- /geokey/core/context_processors.py: -------------------------------------------------------------------------------- 1 | """Core context processors.""" 2 | 3 | from django.conf import settings 4 | from django.contrib.sites.shortcuts import get_current_site 5 | 6 | from geokey import version 7 | 8 | 9 | def project_settings(request): 10 | GOOGLE_ANALYTICS = None 11 | 12 | if hasattr(settings, 'GOOGLE_ANALYTICS'): 13 | GOOGLE_ANALYTICS = settings.GOOGLE_ANALYTICS 14 | 15 | return { 16 | 'DEBUG': settings.DEBUG, 17 | 'PLATFORM_NAME': get_current_site(request).name, 18 | 'GEOKEY_VERSION': version.get_version(), 19 | 'GOOGLE_ANALYTICS': GOOGLE_ANALYTICS 20 | } 21 | -------------------------------------------------------------------------------- /geokey/core/exceptions.py: -------------------------------------------------------------------------------- 1 | """Core exceptions.""" 2 | 3 | 4 | class Unauthenticated(Exception): 5 | """Thrown when user is not authenticated.""" 6 | 7 | pass 8 | 9 | 10 | class MalformedRequestData(Exception): 11 | """Thrown when request data is malformed.""" 12 | 13 | pass 14 | 15 | 16 | class InputError(Exception): 17 | """Thrown on input error.""" 18 | 19 | pass 20 | 21 | 22 | class FileTypeError(Exception): 23 | """Thrown on file type error.""" 24 | 25 | pass 26 | -------------------------------------------------------------------------------- /geokey/core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/core/mixins.py: -------------------------------------------------------------------------------- 1 | """Core mixins.""" 2 | 3 | 4 | class FilterMixin(object): 5 | """A mixin for filter.""" 6 | 7 | def remove_filter_field(self, field): 8 | """ 9 | Remove a field from the filter. 10 | 11 | Parameters 12 | ---------- 13 | field : geokey.categories.models.Field 14 | Represents the field of a category. 15 | """ 16 | if self.filters: 17 | category_filter = self.filters.get(str(field.category.id), None) 18 | 19 | if category_filter: 20 | field_filter = category_filter.pop(field.key, None) 21 | 22 | if field_filter: 23 | self.save() 24 | 25 | def save(self, *args, **kwargs): 26 | """Overwrite `save` to implement integrity ensurance.""" 27 | self.where_clause = None 28 | 29 | if self.filters is not None: 30 | queries = [] 31 | 32 | for key in self.filters: 33 | category = self.project.categories.get(pk=key) 34 | queries.append(category.get_query(self.filters[key])) 35 | 36 | if len(queries) > 0: 37 | query = ' OR '.join(queries) 38 | self.where_clause = query 39 | else: 40 | self.where_clause = 'FALSE' 41 | 42 | super(FilterMixin, self).save(*args, **kwargs) 43 | -------------------------------------------------------------------------------- /geokey/core/serializers.py: -------------------------------------------------------------------------------- 1 | """Core serializers.""" 2 | 3 | from rest_framework import serializers 4 | 5 | 6 | class FieldSelectorSerializer(serializers.ModelSerializer): 7 | """ 8 | Field selector serializer. 9 | 10 | Instances accept a `fields` keyword argument to set which fields shall be 11 | serialized. 12 | """ 13 | 14 | def __init__(self, *args, **kwargs): 15 | """Initialization.""" 16 | # Don't pass the `fields` argument to the superclass 17 | fields = kwargs.pop('fields', None) 18 | 19 | # Instantiate the superclass 20 | super(FieldSelectorSerializer, self).__init__(*args, **kwargs) 21 | 22 | if fields: 23 | # Drop any fields that are not specified in the `fields` argument 24 | allowed = set(fields) 25 | existing = set(self.fields.keys()) 26 | for field in existing - allowed: 27 | self.fields.pop(field) 28 | -------------------------------------------------------------------------------- /geokey/core/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/settings/__init__.py -------------------------------------------------------------------------------- /geokey/core/settings/prod.py: -------------------------------------------------------------------------------- 1 | """Production settings.""" 2 | 3 | from .base import * 4 | 5 | # Hosts/domain names that are valid for this site; required if DEBUG is False 6 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 7 | ALLOWED_HOSTS = ['*'] 8 | -------------------------------------------------------------------------------- /geokey/core/signals.py: -------------------------------------------------------------------------------- 1 | """Core signals.""" 2 | 3 | from django.dispatch import Signal 4 | 5 | delete_project = Signal(providing_args=["project"]) 6 | 7 | class RequestAccessorSignal(Signal): 8 | """Request accessor signal.""" 9 | 10 | def __init__(self, providing_args=None): 11 | """Initiate the signal.""" 12 | return Signal.__init__(self, providing_args) 13 | 14 | def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): 15 | """Connect the signal.""" 16 | Signal.connect(self, receiver, sender, weak, dispatch_uid) 17 | 18 | 19 | request_accessor = RequestAccessorSignal() 20 | 21 | 22 | def get_request(): 23 | """Get the current request.""" 24 | entry = request_accessor.send(None) 25 | if entry: 26 | entry = entry[0] 27 | if entry: 28 | return entry[1] 29 | return None 30 | -------------------------------------------------------------------------------- /geokey/core/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | """Custome template tags for GeoKey.""" -------------------------------------------------------------------------------- /geokey/core/templatetags/logger.py: -------------------------------------------------------------------------------- 1 | """Custom GeoKey template tags.""" 2 | 3 | from django import template 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.filter() 9 | def check_media_file_type(media_file_class): 10 | """Check media file type and returns in a human-readble format.""" 11 | if media_file_class == 'AudioFile': 12 | media_file_type = 'Audio file' 13 | elif media_file_class == 'VideoFile': 14 | media_file_type = 'Video file' 15 | elif media_file_class == 'DocumentFile': 16 | media_file_type = 'Document file' 17 | elif media_file_class == 'ImageFile': 18 | media_file_type = 'Image file' 19 | 20 | return media_file_type 21 | 22 | 23 | @register.filter() 24 | def check_field_type(field_class): 25 | """Check field type and returns in a human-readble format.""" 26 | if field_class == 'TextField': 27 | field_type = 'Text field' 28 | elif field_class == 'NumericField': 29 | field_type = 'Numeric field' 30 | elif field_class == 'DateField': 31 | field_type = 'Date field' 32 | elif field_class == 'DateTimeField': 33 | field_type = 'Date & time field' 34 | elif field_class == 'TimeField': 35 | field_type = 'Time field' 36 | elif field_class == 'LookupField': 37 | field_type = 'Select box field' 38 | elif field_class == 'MultipleLookupField': 39 | field_type = 'Multiple select field' 40 | 41 | return field_type 42 | -------------------------------------------------------------------------------- /geokey/core/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/tests/__init__.py -------------------------------------------------------------------------------- /geokey/core/tests/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/tests/helpers/__init__.py -------------------------------------------------------------------------------- /geokey/core/tests/helpers/image_helpers.py: -------------------------------------------------------------------------------- 1 | """Core image helpers.""" 2 | 3 | from PIL import Image 4 | from io import BytesIO 5 | 6 | from django.core.files.base import ContentFile 7 | 8 | 9 | def get_image(file_name='test.png', width=200, height=200): 10 | image_file = BytesIO() 11 | image = Image.new('RGBA', size=(width, height), color=(255, 0, 255)) 12 | image.save(image_file, 'png') 13 | image_file.seek(0) 14 | 15 | the_file = ContentFile(image_file.read(), file_name) 16 | the_file.content_type = 'image/png' 17 | 18 | return the_file 19 | -------------------------------------------------------------------------------- /geokey/core/tests/helpers/render_helpers.py: -------------------------------------------------------------------------------- 1 | """Core render helpers.""" 2 | 3 | from re import sub 4 | 5 | 6 | def remove_csrf(decoded_input): 7 | csrf_regex = r']+csrfmiddlewaretoken[^>]+>' 8 | return sub(csrf_regex, '', decoded_input) 9 | -------------------------------------------------------------------------------- /geokey/core/tests/logger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/tests/logger/__init__.py -------------------------------------------------------------------------------- /geokey/core/tests/logger/test_log_user.py: -------------------------------------------------------------------------------- 1 | """Tests for logger: model User.""" 2 | 3 | from django.test import TestCase 4 | 5 | from geokey.core.models import LoggerHistory 6 | from geokey.users.tests.model_factories import UserFactory 7 | 8 | 9 | class LogUserTest(TestCase): 10 | """Test model User.""" 11 | 12 | def setUp(self): 13 | """Set up test.""" 14 | self.user = UserFactory.create() 15 | 16 | def test_log_create(self): 17 | """Test when user gets created.""" 18 | log_count_init = LoggerHistory.objects.count() 19 | UserFactory.create() 20 | self.assertEqual(LoggerHistory.objects.count(), log_count_init) 21 | 22 | def test_log_update_display_name(self): 23 | """Test when display name changes.""" 24 | log_count_init = LoggerHistory.objects.count() 25 | self.user.display_name = '%s UPDATED' % self.user.display_name 26 | self.user.save() 27 | self.assertEqual(LoggerHistory.objects.count(), log_count_init) 28 | -------------------------------------------------------------------------------- /geokey/core/tests/test_urls.py: -------------------------------------------------------------------------------- 1 | """Tests for URLs.""" 2 | 3 | from django.test import TestCase 4 | from django.core.urlresolvers import reverse, resolve 5 | 6 | from geokey.core.views import InfoAPIView 7 | 8 | 9 | class UrlsTest(TestCase): 10 | """Test all URLs.""" 11 | 12 | def test_reverse_info(self): 13 | """Test reverser for info API.""" 14 | self.assertEqual(reverse('api:info'), '/api/info/') 15 | 16 | def test_resolve_subset_list(self): 17 | """Test resolver for info API.""" 18 | resolved = resolve('/api/info/') 19 | self.assertEqual(resolved.func.__name__, InfoAPIView.__name__) 20 | -------------------------------------------------------------------------------- /geokey/core/url/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/core/url/__init__.py -------------------------------------------------------------------------------- /geokey/core/urls.py: -------------------------------------------------------------------------------- 1 | """Core URLs.""" 2 | 3 | from django.conf import settings 4 | from django.conf.urls import include, url 5 | from django.conf.urls.static import static 6 | from django.views.generic.base import RedirectView 7 | 8 | 9 | urlpatterns = [ 10 | url( 11 | r'^oauth2/', 12 | include('oauth2_provider.urls', namespace='oauth2_provider') 13 | ), 14 | url( 15 | r'^admin/', 16 | include('geokey.core.url.admin', namespace='admin') 17 | ), 18 | url( 19 | r'^admin/account/', 20 | include('allauth.urls') 21 | ), 22 | url( 23 | r'^ajax/', 24 | include('geokey.core.url.ajax', namespace='ajax') 25 | ), 26 | url( 27 | r'^api/', 28 | include('geokey.core.url.api', namespace='api') 29 | ), 30 | url( 31 | r'^', 32 | include('geokey.extensions.urls') 33 | ), 34 | url( 35 | r'^$', 36 | RedirectView.as_view(url='/admin/', permanent=True) 37 | ), 38 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 39 | 40 | 41 | if settings.DEBUG and settings.DEBUG_TOOLBAR: 42 | import debug_toolbar 43 | urlpatterns += [ 44 | url(r'^debug-toolbar/', include(debug_toolbar.urls)), 45 | ] 46 | -------------------------------------------------------------------------------- /geokey/extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/extensions/__init__.py -------------------------------------------------------------------------------- /geokey/extensions/base.py: -------------------------------------------------------------------------------- 1 | """Base for extensions.""" 2 | 3 | from geokey.extensions.exceptions import ExtensionExists 4 | 5 | 6 | extensions = {} 7 | 8 | 9 | def register(ext_id, name, display_admin=False, superuser=False, version=None): 10 | """ 11 | Register a new extension on the system. 12 | 13 | Parameters 14 | ---------- 15 | ext_id : str 16 | Unique identifier for the extension. 17 | name : str 18 | Human readable name of the extension. 19 | display_admin : bool 20 | Indicates if the extension provides pages for the admin interface. 21 | superuser : bool 22 | Indicates if the extentsion is available for superusers only. 23 | version : str 24 | Version of the extension (optional). 25 | 26 | Raises 27 | ------ 28 | ExtensionExists 29 | When another extension with the same `ext_id` has already been 30 | registered. 31 | """ 32 | if ext_id in extensions.keys(): 33 | raise ExtensionExists( 34 | 'An extension with ID %s has already been registered.' % ext_id 35 | ) 36 | 37 | extensions[ext_id] = { 38 | 'ext_id': ext_id, 39 | 'name': name, 40 | 'version': version, 41 | 'display_admin': display_admin, 42 | 'superuser': superuser, 43 | 'index_url': ext_id + ':index' 44 | } 45 | 46 | 47 | def deregister(ext_id): 48 | """ 49 | Deregister an extension from the system. 50 | 51 | Only to be used for testing. 52 | 53 | Parameters 54 | ---------- 55 | ext_id : str 56 | Unique identifier for the extension. 57 | """ 58 | extensions.pop(ext_id, None) 59 | -------------------------------------------------------------------------------- /geokey/extensions/exceptions.py: -------------------------------------------------------------------------------- 1 | """Exceptions for extensions.""" 2 | 3 | 4 | class ExtensionExists(BaseException): 5 | """Thrown when extension already exists.""" 6 | 7 | pass 8 | -------------------------------------------------------------------------------- /geokey/extensions/mixins.py: -------------------------------------------------------------------------------- 1 | """Mixins for extensions.""" 2 | 3 | from geokey.superusertools.mixins import SuperuserMixin 4 | 5 | 6 | class SuperuserMixin(SuperuserMixin): 7 | """A mixin for superuser.""" 8 | 9 | exception_message = 'This extension is for superusers only.' 10 | -------------------------------------------------------------------------------- /geokey/extensions/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/extensions/tests/__init__.py -------------------------------------------------------------------------------- /geokey/extensions/tests/test_base.py: -------------------------------------------------------------------------------- 1 | """Tests for base of extensions.""" 2 | 3 | from django.test import TestCase 4 | 5 | from geokey.extensions.base import extensions, register, deregister 6 | from geokey.extensions.exceptions import ExtensionExists 7 | 8 | 9 | class RegisterTest(TestCase): 10 | """Test register.""" 11 | 12 | def tearDown(self): 13 | """Tear down test.""" 14 | deregister(self.ext_id) 15 | 16 | def test_register(self): 17 | """Test registering new extension.""" 18 | self.ext_id = 'test_ext' 19 | register(self.ext_id, 'Test', True, True, '1.0.0') 20 | 21 | extension = extensions.get(self.ext_id) 22 | self.assertEqual(extension.get('ext_id'), self.ext_id) 23 | self.assertEqual(extension.get('name'), 'Test') 24 | self.assertEqual(extension.get('version'), '1.0.0') 25 | self.assertTrue(extension.get('display_admin')) 26 | self.assertTrue(extension.get('superuser')) 27 | self.assertEqual(extension.get('index_url'), self.ext_id + ':index') 28 | 29 | def test_register_when_already_exists(self): 30 | """Test registering existing extension.""" 31 | self.ext_id = 'test_ext' 32 | extensions[self.ext_id] = { 33 | 'ext_id': self.ext_id, 34 | 'name': 'Test', 35 | 'version': '1.0.0', 36 | 'display_admin': True, 37 | 'superuser': True, 38 | 'index_url': self.ext_id + ':index' 39 | } 40 | 41 | with self.assertRaises(ExtensionExists): 42 | register(self.ext_id, 'Test B', False, False, '1.0.0') 43 | 44 | 45 | class DeregisterTest(TestCase): 46 | """Test deregister.""" 47 | 48 | def test_deregister(self): 49 | """Test deregistering existing extension.""" 50 | ext_id = 'test_ext' 51 | extensions[ext_id] = { 52 | 'ext_id': ext_id, 53 | 'name': 'Test', 54 | 'version': '1.0.0', 55 | 'display_admin': True, 56 | 'superuser': True, 57 | 'index_url': ext_id + ':index' 58 | } 59 | deregister(ext_id) 60 | 61 | self.assertNotIn(ext_id, extensions) 62 | -------------------------------------------------------------------------------- /geokey/extensions/tests/test_mixins.py: -------------------------------------------------------------------------------- 1 | """Tests for mixins of extensions.""" 2 | 3 | from django.test import TestCase 4 | from django.views.generic import View 5 | from django.views.generic.base import TemplateResponseMixin 6 | 7 | from rest_framework.test import APIRequestFactory 8 | 9 | from geokey.users.tests.model_factories import UserFactory 10 | from geokey.extensions.mixins import SuperuserMixin 11 | 12 | 13 | class ExampleView(SuperuserMixin, TemplateResponseMixin, View): 14 | """Set up example view.""" 15 | 16 | template_name = 'base.html' 17 | 18 | def get(self, request): 19 | """Set up GET request.""" 20 | return self.render_to_response({ 21 | 'country': 'United Kingdom' 22 | }) 23 | 24 | 25 | class SuperuserMixinTest(TestCase): 26 | """Test superuser mixin.""" 27 | 28 | def setUp(self): 29 | """Set up test.""" 30 | self.view = ExampleView.as_view() 31 | self.request = APIRequestFactory().get('http://example.com') 32 | 33 | def test_with_user(self): 34 | """Test with user.""" 35 | self.request.user = UserFactory.create(**{'is_superuser': False}) 36 | response = self.view(self.request).render() 37 | 38 | self.assertEqual(response.status_code, 200) 39 | self.assertContains( 40 | response, 41 | 'This extension is for superusers only.' 42 | ) 43 | 44 | def test_with_superuser(self): 45 | """Test with superuser.""" 46 | self.request.user = UserFactory.create(**{'is_superuser': True}) 47 | response = self.view(self.request).render() 48 | 49 | self.assertEqual(response.status_code, 200) 50 | self.assertNotContains( 51 | response, 52 | 'This extension is for superusers only.' 53 | ) 54 | -------------------------------------------------------------------------------- /geokey/extensions/urls.py: -------------------------------------------------------------------------------- 1 | """URLs for extensions.""" 2 | 3 | from os.path import dirname, isfile, join 4 | 5 | from django.conf.urls import url, include 6 | 7 | from geokey.extensions.base import extensions 8 | 9 | 10 | urlpatterns = [] 11 | 12 | for extension in extensions: 13 | if isfile(join(dirname(__import__(extension).__file__), 'urls.py')): 14 | urls = '%s.urls' % extension 15 | urlpatterns.append(url(r'^', include(urls, namespace=extension))) 16 | -------------------------------------------------------------------------------- /geokey/projects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/projects/__init__.py -------------------------------------------------------------------------------- /geokey/projects/base.py: -------------------------------------------------------------------------------- 1 | """Base for projects.""" 2 | 3 | from model_utils import Choices 4 | 5 | from django.conf import settings 6 | 7 | 8 | STATUS = Choices('active', 'inactive', 'deleted') 9 | allowed = ("auth", "false", "true") 10 | if hasattr(settings, 'ALLOWED_CONTRIBUTORS'): 11 | allowed = settings.ALLOWED_CONTRIBUTORS 12 | EVERYONE_CONTRIBUTES = Choices(*allowed) 13 | -------------------------------------------------------------------------------- /geokey/projects/forms.py: -------------------------------------------------------------------------------- 1 | """Forms for projects.""" 2 | 3 | from django import forms 4 | 5 | from .models import Project 6 | 7 | 8 | class ProjectCreateForm(forms.ModelForm): 9 | """ 10 | Validates the inputs against the model definition. 11 | Used in .views.ProjectAdminCreateView 12 | """ 13 | class Meta: 14 | model = Project 15 | fields = ('name', 'description', 'isprivate', 'islocked', 16 | 'everyone_contributes') 17 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import django.contrib.gis.db.models.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Admins', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 19 | ('contact', models.BooleanField(default=True)), 20 | ], 21 | options={ 22 | 'ordering': ['project__name'], 23 | }, 24 | bases=(models.Model,), 25 | ), 26 | migrations.CreateModel( 27 | name='Project', 28 | fields=[ 29 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 30 | ('name', models.CharField(max_length=100)), 31 | ('description', models.TextField(null=True, blank=True)), 32 | ('isprivate', models.BooleanField(default=False)), 33 | ('created_at', models.DateTimeField(auto_now_add=True)), 34 | ('everyone_contributes', models.BooleanField(default=True)), 35 | ('status', models.CharField(default=b'active', max_length=20, choices=[(b'active', b'active'), (b'inactive', b'inactive'), (b'deleted', b'deleted')])), 36 | ('geographic_extend', django.contrib.gis.db.models.fields.PolygonField(srid=4326, null=True, geography=True)), 37 | ], 38 | options={ 39 | 'ordering': ['name'], 40 | }, 41 | bases=(models.Model,), 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0002_auto_20150106_1338.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('projects', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='project', 18 | name='admins', 19 | field=models.ManyToManyField(related_name='admins', through='projects.Admins', to=settings.AUTH_USER_MODEL), 20 | preserve_default=True, 21 | ), 22 | migrations.AddField( 23 | model_name='project', 24 | name='creator', 25 | field=models.ForeignKey(to=settings.AUTH_USER_MODEL), 26 | preserve_default=True, 27 | ), 28 | migrations.AddField( 29 | model_name='admins', 30 | name='project', 31 | field=models.ForeignKey(related_name='admin_of', to='projects.Project'), 32 | preserve_default=True, 33 | ), 34 | migrations.AddField( 35 | model_name='admins', 36 | name='user', 37 | field=models.ForeignKey(related_name='has_admins', to=settings.AUTH_USER_MODEL), 38 | preserve_default=True, 39 | ), 40 | migrations.AlterUniqueTogether( 41 | name='admins', 42 | unique_together=set([('project', 'user')]), 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0003_auto_20150123_1148.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('projects', '0002_auto_20150106_1338'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='project', 16 | name='everyone_contributes', 17 | field=models.CharField(default=b'Auth', max_length=20, choices=[(b'True', b'True'), (b'Auth', b'Auth'), (b'False', b'False')]), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0004_auto_20150123_1507.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('projects', '0003_auto_20150123_1148'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='project', 16 | name='everyone_contributes', 17 | field=models.CharField(default=b'auth', max_length=20, choices=[(b'true', b'true'), (b'auth', b'auth'), (b'false', b'false')]), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0005_auto_20150202_1041.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('projects', '0004_auto_20150123_1507'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL("DELETE FROM projects_admins WHERE project_id IN (SELECT id FROM projects_project WHERE status = 'deleted');") 15 | ] 16 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0006_remove_admins_contact.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('projects', '0005_auto_20150202_1041'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='admins', 16 | name='contact', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /geokey/projects/migrations/0007_auto_20160122_1409.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('projects', '0006_remove_admins_contact'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RenameField( 15 | model_name='project', 16 | old_name='geographic_extend', 17 | new_name='geographic_extent', 18 | ), 19 | migrations.AddField( 20 | model_name='project', 21 | name='islocked', 22 | field=models.BooleanField(default=False), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /geokey/projects/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/projects/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/projects/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/projects/templatetags/__init__.py -------------------------------------------------------------------------------- /geokey/projects/templatetags/count.py: -------------------------------------------------------------------------------- 1 | """Template tags for counting.""" 2 | 3 | from django import template 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.simple_tag 9 | def more_link_text(count, singular, plural, minus=5): 10 | return 'Show {more_count} more {label}'.format( 11 | more_count=count-minus, 12 | label=(plural if count-minus != 1 else singular) 13 | ) 14 | -------------------------------------------------------------------------------- /geokey/projects/templatetags/project_attributes.py: -------------------------------------------------------------------------------- 1 | """Template tags for project attributes.""" 2 | 3 | from django import template 4 | from django.template.loader import render_to_string 5 | 6 | 7 | register = template.Library() 8 | 9 | 10 | @register.simple_tag 11 | def project_attributes(project): 12 | return render_to_string( 13 | 'projects/project_attributes.html', 14 | { 15 | 'creator': project.creator.display_name, 16 | 'created_at': project.created_at.strftime("%d %B %Y, %H:%M"), 17 | 'private_label': ('Private' if project.isprivate else 'Public'), 18 | 'inactive': project.status == 'inactive' 19 | } 20 | ) 21 | -------------------------------------------------------------------------------- /geokey/projects/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/projects/tests/__init__.py -------------------------------------------------------------------------------- /geokey/projects/tests/test_serializers.py: -------------------------------------------------------------------------------- 1 | """Tests for serializers of projects.""" 2 | 3 | from django.test import TestCase 4 | from django.contrib.auth.models import AnonymousUser 5 | 6 | from geokey.users.tests.model_factories import UserFactory 7 | from geokey.contributions.tests.model_factories import ObservationFactory 8 | 9 | from .model_factories import ProjectFactory 10 | from ..serializers import ProjectSerializer 11 | 12 | 13 | class SerializerTest(TestCase): 14 | def test_get_subsets(self): 15 | project = ProjectFactory.create() 16 | serializer = ProjectSerializer(project) 17 | self.assertIsNotNone(serializer.get_subsets(project)) 18 | 19 | def test_get_geographic_extent(self): 20 | project = ProjectFactory.create(**{'geographic_extent': None}) 21 | serializer = ProjectSerializer(project) 22 | self.assertIsNone(serializer.get_geographic_extent(project)) 23 | 24 | project = ProjectFactory.create() 25 | serializer = ProjectSerializer(project) 26 | self.assertIsNotNone(serializer.get_geographic_extent(project)) 27 | 28 | def test_get_user_contributions(self): 29 | user = UserFactory.create() 30 | project = ProjectFactory.create() 31 | ObservationFactory.create_batch( 32 | 5, 33 | **{'creator': user, 'project': project} 34 | ) 35 | serializer = ProjectSerializer(project, context={'user': user}) 36 | self.assertEqual(5, serializer.get_user_contributions(project)) 37 | 38 | serializer = ProjectSerializer( 39 | project, context={'user': AnonymousUser()} 40 | ) 41 | self.assertEqual(0, serializer.get_user_contributions(project)) 42 | -------------------------------------------------------------------------------- /geokey/socialinteractions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/socialinteractions/__init__.py -------------------------------------------------------------------------------- /geokey/socialinteractions/base.py: -------------------------------------------------------------------------------- 1 | """Base for categories.""" 2 | 3 | from model_utils import Choices 4 | 5 | STATUS = Choices('active', 'inactive') 6 | 7 | FREQUENCY = Choices('5min', 8 | '10min', 9 | '20min', 10 | '30min', 11 | 'hourly', 12 | 'daily', 13 | 'weekly', 14 | 'fortnightly', 15 | 'monthly') 16 | 17 | freq_dic = { 18 | '5min': 0.083, 19 | '10min': 0.17, 20 | '20min': 0.33, 21 | '30min': 0.5, 22 | 'hourly': 1, 23 | 'daily': 24, 24 | 'weekly': 168, 25 | 'fortnightly': 336, 26 | 'monthly': 672 27 | } 28 | -------------------------------------------------------------------------------- /geokey/socialinteractions/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/socialinteractions/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/socialinteractions/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/socialinteractions/templatetags/__init__.py -------------------------------------------------------------------------------- /geokey/socialinteractions/templatetags/placeholder_filters.py: -------------------------------------------------------------------------------- 1 | """Custom placeholder template filters""" 2 | 3 | from django import template 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.filter() 9 | def project_replace(value, project_name): 10 | return value.replace('$project$', hashify(project_name)) 11 | 12 | 13 | @register.filter() 14 | def hashify(value): 15 | return "#" + value.replace(' ', '').replace('-', '').lower() 16 | 17 | 18 | @register.filter() 19 | def add_link(value, link): 20 | link = '' + link + '' 21 | value = value.replace("$link$", link) 22 | return value 23 | -------------------------------------------------------------------------------- /geokey/socialinteractions/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/socialinteractions/tests/__init__.py -------------------------------------------------------------------------------- /geokey/socialinteractions/tests/model_factories.py: -------------------------------------------------------------------------------- 1 | """Model factories used for tests of socialinteractions.""" 2 | 3 | import factory 4 | 5 | from allauth.socialaccount.models import SocialAccount 6 | 7 | from geokey.users.tests.model_factories import UserFactory 8 | from geokey.projects.tests.model_factories import ProjectFactory 9 | 10 | from ..models import SocialInteractionPost, SocialInteractionPull 11 | 12 | 13 | class SocialInteractionFactory(factory.django.DjangoModelFactory): 14 | class Meta: 15 | model = SocialInteractionPost 16 | 17 | creator = factory.SubFactory(UserFactory) 18 | text_to_post = 'Text to post including $link$' 19 | link = 'www.link.com' 20 | project = factory.SubFactory(ProjectFactory) 21 | status = 'active' 22 | socialaccount = SocialAccount() 23 | 24 | 25 | class SocialInteractionPullFactory(factory.django.DjangoModelFactory): 26 | class Meta: 27 | model = SocialInteractionPull 28 | 29 | creator = factory.SubFactory(UserFactory) 30 | text_to_pull = '#Project2' 31 | project = factory.SubFactory(ProjectFactory) 32 | status = 'active' 33 | socialaccount = SocialAccount() 34 | frequency = '5min' 35 | -------------------------------------------------------------------------------- /geokey/static/img/ajax-loader-blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/img/ajax-loader-blue.gif -------------------------------------------------------------------------------- /geokey/static/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/img/ajax-loader.gif -------------------------------------------------------------------------------- /geokey/static/img/blurry.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/img/blurry.jpg -------------------------------------------------------------------------------- /geokey/static/img/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/img/play.png -------------------------------------------------------------------------------- /geokey/static/img/providers/facebook.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /geokey/static/img/providers/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /geokey/static/img/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/img/success.png -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.category.display.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Initialises the color picker. 3 | * Initialises the file upload and sets the initial state. 4 | * 5 | * Docs for colorpicker: 6 | * http://mjolnic.com/bootstrap-colorpicker/ 7 | * 8 | * Docs for file upload: 9 | * http://plugins.krajee.com/file-input 10 | * 11 | * Used in: 12 | * - templates/categories/category_display.html 13 | * ***********************************************/ 14 | 15 | $(function() { 16 | 'use strict'; 17 | 18 | // Initialise the color picker 19 | $('#colour').colorpicker({ 20 | format: 'hex' 21 | }); 22 | 23 | // Initialise file upload for each field 24 | $('input:file').each(function() { 25 | Ui.FileInput.init($(this)); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.field.create.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Based on what is selected as field type, the script shows and hides form 3 | * fields specific to certain field types. 4 | * 5 | * For instance, if you select the type TextField, the inputs for max lenght and 6 | * display as textbox are show. These fields are wrapped in a div with 7 | * id="text", which is shown. 8 | * 9 | * Used in: 10 | * - templates/categories/field_create.html 11 | * ***********************************************/ 12 | 13 | (function () { 14 | 'use strict'; 15 | 16 | function handleTypeSelect(event) { 17 | // hide all specific inputs 18 | $('.field-special').addClass('hidden'); 19 | 20 | // switch on the field type, it shows specific fields accordingly 21 | switch (event.target.value) { 22 | case 'TextField': 23 | $('#text').removeClass('hidden'); 24 | break; 25 | case 'NumericField': 26 | $('#minmax').removeClass('hidden'); 27 | break; 28 | case 'LookupField': 29 | case 'MultipleLookupField': 30 | $('#lookup').removeClass('hidden'); 31 | break; 32 | } 33 | } 34 | 35 | // register the event handler on the select field 36 | $('form select#type').change(handleTypeSelect); 37 | }()); 38 | -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.field.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Module to edit field properties. Is currently only responsible 3 | * for setting minimum and maximum values of numeric fields and initialising 4 | * lookup field edits. 5 | * Is automatically loaded when included in a page. 6 | 7 | * Used in: 8 | * - templates/categories/field_settings.html 9 | * ***********************************************/ 10 | 11 | $(function() { 12 | 'use strict'; 13 | 14 | var projectId = $('body').attr('data-project-id'), 15 | categoryId = $('body').attr('data-category-id'), 16 | fieldId = $('body').attr('data-field-id'), 17 | url = 'projects/' + projectId + '/categories/' + categoryId + '/fields/' + fieldId; 18 | 19 | // Initialize the lookup panel functionality if the field is a lookup 20 | if ($('#lookupValuesPanel').length !== 0) { 21 | new Ui.LookupPanel('#lookupValuesPanel', url); 22 | 23 | // Initialise file upload for each field 24 | $('input:file').each(function() { 25 | Ui.FileInput.init($(this)); 26 | }); 27 | } 28 | 29 | // Set the min and maximum values for numeric fields, so they can be 30 | // validated 31 | function handleNumericFieldEdit(event) { 32 | var target = $(event.target); 33 | 34 | if (target.attr('id') === 'minval') { 35 | // set the current minval as minimum value for maxval field 36 | $('form input#maxval').attr('min', target.val()); 37 | } else if (target.attr('id') === 'maxval') { 38 | // set the current maxval as minimum value for minval field 39 | $('form input#minval').attr('max', target.val()); 40 | } 41 | } 42 | 43 | $('form input[type="number"]').change(handleNumericFieldEdit); 44 | }); 45 | -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.fileinput.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Initialises the file input functionality for 3 | * input field. 4 | * 5 | * Used in: 6 | * - templates/categories/category_display.html 7 | * - templates/categories/field_settings.html 8 | * ***********************************************/ 9 | 10 | $(function(global) { 11 | 'use strict'; 12 | 13 | function FileInput() {} 14 | 15 | FileInput.prototype.init = function(field, additionalSettings) { 16 | var settings = { 17 | showUpload: false, 18 | showCancel: false, 19 | browseLabel: 'Browse...', 20 | msgLoading: 'Loading file {index} of {files}...' 21 | }; 22 | 23 | for (var key in additionalSettings) { 24 | settings[key] = additionalSettings[key]; 25 | } 26 | 27 | if (field.attr('data-preview')) { 28 | settings.initialPreview = ''; 29 | } 30 | 31 | field.fileinput(settings); 32 | field.parents().find('div.fileinput-remove').hide(); 33 | 34 | field.on('fileclear', function() { 35 | $('input#' + $(this).data('target') + '-clear').val('true'); 36 | }); 37 | }; 38 | 39 | global.FileInput = new FileInput(); 40 | }(window.Ui ? window.Ui : window.Ui = {})); 41 | -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.project.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Changes the display of radio buttons for 3 | * contributing permissions on a project. 4 | * 5 | * Used in: 6 | * - projects/project_create.html 7 | * ***********************************************/ 8 | 9 | $(function() { 10 | 'use strict'; 11 | 12 | $('input[name="isprivate"]').change(function(event) { 13 | if ($(event.target).attr('id') === 'public') { 14 | $('.public').removeClass('hidden'); 15 | $('.private').addClass('hidden'); 16 | } else if ($(event.target).attr('id') === 'private') { 17 | $('.public').addClass('hidden'); 18 | $('.private').removeClass('hidden'); 19 | } 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.socialinteractions.twittervalidator.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Indicates to user how many characters are still 3 | * left in the post to Twitter 4 | * 5 | * if selected #socialaccount is twitter: 6 | * #remaining_characters are calculated based on fields: #text_post & #text_link 7 | * maxlength attribute in #text_post is dynamically updated 8 | * else #characters is hidden 9 | * 10 | * Used in: 11 | * - templates/socialinteractions/socialinteraction_post_create.html 12 | * - templates/socialinteractions/socialinteraction_post_settings.html 13 | * 14 | * ***********************************************/ 15 | 16 | if ($("#socialaccount option:selected").text().match("¦¦ twitter$")) { 17 | $("#text_post").on('input', function () { 18 | update_length() 19 | }).trigger('input') 20 | 21 | $("#text_link").on('input', function () { 22 | update_length() 23 | }).trigger('input') 24 | 25 | function update_length() { 26 | if (/\$link\$/i.test($("#text_post").val())) { 27 | post_length = $("#text_post").val().length - "\$link\$".length 28 | url_length = $('#text_link').val().length 29 | if (/\$project_id\$/i.test($('#text_link').val())) 30 | url_length = url_length - "\$project_id\$".length + $('body').data('project-id').toString().length 31 | if (/\$contribution_id\$/i.test($('#text_link').val())) 32 | url_length = url_length - "\$contribution_id\$".length + $('body').data('contributions-count').toString().length + 1 33 | } else { 34 | post_length = $("#text_post").val().length 35 | url_length = 0 36 | } 37 | var remaining = 280 - post_length - url_length 38 | $('#remaining_characters').text(remaining) 39 | $('#text_post').attr('maxlength', $("#text_post").val().length + remaining) 40 | } 41 | 42 | } 43 | 44 | else { 45 | $('#characters').hide() 46 | } 47 | -------------------------------------------------------------------------------- /geokey/static/js/admin.ui.usergroup.permissions.js: -------------------------------------------------------------------------------- 1 | /* *********************************************** 2 | * Based on changes to input[name="permission"}, the script displays (or hides) 3 | * a message that specific settings will be overwritten by project settings, 4 | * i.e. when all users can contribute to the project. 5 | * 6 | * Used in: 7 | * - templates/users/usergroup_permissions.html 8 | * ***********************************************/ 9 | 10 | (function() { 11 | var projectId = $('body').attr('data-project-id'); 12 | var projectPrivate = $('body').attr('data-project-private'); 13 | var everyoneContributes = $('body').attr('data-project-everyone-contributes'); 14 | 15 | function handlePermissionChange(event) { 16 | if ($(this).val() === 'read_only' && everyoneContributes !== 'false') { 17 | var add = ''; 18 | if (projectPrivate !== 'True' && everyoneContributes === 'auth') { 19 | add = ', who have access to this project,'; 20 | } 21 | $('form#permissions').before('
Currently, all users' + add + ' can contribute to the project. This setting overwrites permissions of individual user groups. If you plan to restrict contributing permissions to certain user groups, head to project settings first and change the project permissions.
'); 22 | } else { 23 | $('.hint').remove(); 24 | } 25 | } 26 | 27 | $('form#permissions input[name="permission"]').change(handlePermissionChange); 28 | }()); 29 | -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-colorpicker/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Stefan Petre 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha.png -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/hue-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/hue-horizontal.png -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/hue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/hue.png -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/saturation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-colorpicker/img/bootstrap-colorpicker/saturation.png -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-datetimepicker/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jonathan Peterson (@Eonasdan) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-fileinput/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 - 2016, Kartik Visweswaran 2 | Krajee.com 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | * Neither the names of Kartik Visweswaran or Krajee nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-fileinput/img/loading-sm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-fileinput/img/loading-sm.gif -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap-fileinput/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap-fileinput/img/loading.gif -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2016 Twitter, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/static/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /geokey/static/lib/handlebars/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011-2016 by Yehuda Katz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /geokey/static/lib/jquery/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /geokey/static/lib/modernizr/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 by Modernizr (http://modernizr.com) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /geokey/static/lib/moment/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2016 Tim Wood, Iskren Chernev, Moment.js contributors 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /geokey/subsets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/subsets/__init__.py -------------------------------------------------------------------------------- /geokey/subsets/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | try: 6 | from django.contrib.postgres.fields import JSONField 7 | except ImportError: 8 | from django_pgjson.fields import JsonBField as JSONField 9 | from django.conf import settings 10 | import geokey.core.mixins 11 | 12 | 13 | class Migration(migrations.Migration): 14 | 15 | dependencies = [ 16 | ('projects', '0005_auto_20150202_1041'), 17 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name='Subset', 23 | fields=[ 24 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 25 | ('name', models.CharField(max_length=100)), 26 | ('description', models.TextField(null=True, blank=True)), 27 | ('created_at', models.DateTimeField(auto_now_add=True)), 28 | ('filters', JSONField(null=True, blank=True)), 29 | ('where_clause', models.TextField(null=True, blank=True)), 30 | ('creator', models.ForeignKey(to=settings.AUTH_USER_MODEL)), 31 | ('project', models.ForeignKey(related_name='subsets', to='projects.Project')), 32 | ], 33 | options={ 34 | }, 35 | bases=(geokey.core.mixins.FilterMixin, models.Model), 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /geokey/subsets/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/subsets/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/subsets/models.py: -------------------------------------------------------------------------------- 1 | """Models for subsets.""" 2 | 3 | from django.conf import settings 4 | from django.db import models 5 | 6 | try: 7 | from django.contrib.postgres.fields import JSONField 8 | except ImportError: 9 | from django_pgjson.fields import JsonBField as JSONField 10 | from simple_history.models import HistoricalRecords 11 | 12 | 13 | from geokey.core.mixins import FilterMixin 14 | 15 | 16 | class Subset(FilterMixin, models.Model): 17 | """Stores a single subset.""" 18 | 19 | name = models.CharField(max_length=100) 20 | description = models.TextField(blank=True, null=True) 21 | created_at = models.DateTimeField(auto_now_add=True) 22 | creator = models.ForeignKey(settings.AUTH_USER_MODEL) 23 | project = models.ForeignKey('projects.Project', related_name='subsets') 24 | filters = JSONField(blank=True, null=True) 25 | where_clause = models.TextField(blank=True, null=True) 26 | history = HistoricalRecords() 27 | -------------------------------------------------------------------------------- /geokey/subsets/serializers.py: -------------------------------------------------------------------------------- 1 | """Serializers for subsets.""" 2 | 3 | from rest_framework.serializers import ModelSerializer 4 | 5 | from geokey.subsets.models import Subset 6 | 7 | 8 | class SubsetSerializer(ModelSerializer): 9 | """Serializer for a subset.""" 10 | 11 | class Meta: 12 | """Serializer meta.""" 13 | 14 | model = Subset 15 | depth = 1 16 | fields = ('id', 'name', 'description', 'created_at') 17 | -------------------------------------------------------------------------------- /geokey/subsets/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/subsets/tests/__init__.py -------------------------------------------------------------------------------- /geokey/subsets/tests/model_factories.py: -------------------------------------------------------------------------------- 1 | """Model factories used for tests of subsets.""" 2 | 3 | import factory 4 | 5 | from geokey.users.tests.model_factories import UserFactory 6 | from geokey.projects.tests.model_factories import ProjectFactory 7 | 8 | from ..models import Subset 9 | 10 | 11 | class SubsetFactory(factory.django.DjangoModelFactory): 12 | class Meta: 13 | model = Subset 14 | 15 | name = factory.Sequence(lambda n: 'project %d' % n) 16 | description = factory.LazyAttribute(lambda o: '%s description' % o.name) 17 | creator = factory.SubFactory(UserFactory) 18 | project = factory.SubFactory(ProjectFactory) 19 | -------------------------------------------------------------------------------- /geokey/superusertools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/superusertools/__init__.py -------------------------------------------------------------------------------- /geokey/superusertools/base.py: -------------------------------------------------------------------------------- 1 | """Base for superuser tools.""" 2 | 3 | from rest_framework.permissions import BasePermission 4 | 5 | 6 | class IsSuperuser(BasePermission): 7 | """A permission to check if user is a superuser.""" 8 | 9 | def has_permission(self, request, view): 10 | """ 11 | Check if user is a superuser. 12 | 13 | Parameters 14 | ---------- 15 | request : rest_framework.request.Request 16 | Object representing the request. 17 | view : rest_framework.views.APIView 18 | View that called the permission. 19 | 20 | Returns 21 | ------- 22 | Boolean 23 | Indicating if user is a superuser. 24 | """ 25 | return request.user and request.user.is_superuser 26 | -------------------------------------------------------------------------------- /geokey/superusertools/mixins.py: -------------------------------------------------------------------------------- 1 | """Mixins for superuser tools.""" 2 | 3 | 4 | class SuperuserMixin(object): 5 | """A mixin for superuser.""" 6 | 7 | exception_message = 'No rights to access superuser tools.' 8 | 9 | def dispatch(self, request, *args, **kwargs): 10 | """ 11 | Dispatch the request. 12 | 13 | Check if user is a superuser, display an error message if not. 14 | 15 | Parameters 16 | ---------- 17 | request : django.http.HttpRequest 18 | Object representing the request. 19 | 20 | Returns 21 | ------- 22 | django.http.HttpResponse 23 | """ 24 | if not request.user.is_superuser: 25 | return self.render_to_response({ 26 | 'error': 'Permission denied.', 27 | 'error_description': self.exception_message 28 | }) 29 | 30 | return super(SuperuserMixin, self).dispatch(request, *args, **kwargs) 31 | -------------------------------------------------------------------------------- /geokey/superusertools/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/superusertools/tests/__init__.py -------------------------------------------------------------------------------- /geokey/templates/account/account_inactive.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Inactive account{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
12 | {% include 'snippets/messages.html' %} 13 | 14 |
15 |

Your account is inactive; likely because you have not confirmed your email address yet. We've emailed you new instructions for verifying your email address. Follow the link provided to activate your account. If you don't receive an email, please make sure you've checked your spam folder.

16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/account/email/email_confirmation_message.txt: -------------------------------------------------------------------------------- 1 | Dear {{ user.display_name }}, 2 | 3 | {% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name %}User {{ user_display }} at {{ site_name }} has given this as an email address. 4 | 5 | To confirm this is correct, go to {{ activate_url }} 6 | {% endblocktrans %}{% endautoescape %} 7 | -------------------------------------------------------------------------------- /geokey/templates/account/email/email_confirmation_signup_message.txt: -------------------------------------------------------------------------------- 1 | {% include "account/email/email_confirmation_message.txt" %} 2 | -------------------------------------------------------------------------------- /geokey/templates/account/email/email_confirmation_signup_subject.txt: -------------------------------------------------------------------------------- 1 | {% include "account/email/email_confirmation_subject.txt" %} 2 | -------------------------------------------------------------------------------- /geokey/templates/account/email/email_confirmation_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %} 3 | {% blocktrans %}Confirm E-mail Address{% endblocktrans %} 4 | {% endautoescape %} 5 | -------------------------------------------------------------------------------- /geokey/templates/account/email/password_reset_key_message.txt: -------------------------------------------------------------------------------- 1 | Dear {{ user.display_name }}, 2 | 3 | {% load i18n %}{% blocktrans with site_domain=current_site.domain %}You're receiving this e-mail because you or someone else has requested a password for your user account at {{site_domain}}. 4 | It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %} 5 | 6 | {{ password_reset_url }} 7 | 8 | {% if username %}{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %} 9 | 10 | {% endif %}{% blocktrans with site_name=current_site.name %}Thanks for using {{site_name}}!{% endblocktrans %} 11 | -------------------------------------------------------------------------------- /geokey/templates/account/email/password_reset_key_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %} 3 | {% blocktrans %}Password Reset E-mail{% endblocktrans %} 4 | {% endautoescape %} 5 | -------------------------------------------------------------------------------- /geokey/templates/account/email_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Confirm email address{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
12 | {% include 'snippets/messages.html' %} 13 | 14 |
15 |

The email confirmation link was invalid, possibly because it has already expired. Please request a new email confirmation.

16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/account/password_reset.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Reset password{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
12 | {% include 'snippets/messages.html' %} 13 | 14 |
15 |
16 |
17 | {% csrf_token %} 18 | 19 |
20 | 21 | 22 | {% if form.email.errors %}{{ form.email.errors|striptags }}{% endif %} 23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 | {% endblock %} 33 | 34 | {% block libraries %} 35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /geokey/templates/account/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Reset password successful{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
12 | {% include 'snippets/messages.html' %} 13 | 14 |
15 |

We've emailed you instructions for resetting your password. You should be receiving them shortly. If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.

16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/account/password_reset_from_key_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Set new password successful{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
12 | {% include 'snippets/messages.html' %} 13 | 14 |
15 |

Your new password has been set. You may go ahead and sign in now.

16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/account/verification_sent.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Verify email address{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
12 | {% include 'snippets/messages.html' %} 13 | 14 |
15 |

We've emailed you instructions for verifying your email address. You should be receiving them shortly. Follow the link provided to finalise the sign up process. If you don't receive an email, please make sure you've checked your spam folder.

16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/categories/category_navigation.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Category
4 | 5 |

6 | {% if project.islocked %}{% endif %} 7 | {{ category.name }} 8 |

9 |
10 | 11 |
12 | Fields 13 | Display 14 | Settings 15 |
16 |
17 | -------------------------------------------------------------------------------- /geokey/templates/geometries/placemarks.kml: -------------------------------------------------------------------------------- 1 | {% extends 'gis/kml/base.kml' %} 2 | {% load kml_tags %} 3 | 4 | {% block placemarks %} 5 | {% for place in data %} 6 | 7 | {{ place|kml_name }} 8 | {{ place|kml_desc|safe }} 9 | {{ place|kml_geom|safe }} 10 | {{ place|kml_style }} 11 | 12 | {% endfor %} 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_created-field.hbs: -------------------------------------------------------------------------------- 1 | 2 |
The contribution has been created
3 | 4 |
5 | 6 |
7 | 8 |
9 |
10 |
11 | 12 |
13 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_datefield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |
8 | 9 |
10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_datetimefield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |
8 | 9 |
10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_lookupfield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_multiplelookupfield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_numericfield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | 8 |
9 | 10 |
11 | 12 |
13 |
14 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_textfield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/_timefield.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |
8 | 9 |
10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/createdfield.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{> created-field}} 3 |
4 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/field.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
{{name}} ({{key}})
4 | 5 | {{#ifCond fieldtype 'TextField'}} 6 | {{> textfield}} 7 | {{/ifCond}} 8 | {{#ifCond fieldtype 'NumericField'}} 9 | {{> numericfield}} 10 | {{/ifCond}} 11 | {{#ifCond fieldtype 'DateTimeField'}} 12 | {{> datetimefield}} 13 | {{/ifCond}} 14 | {{#ifCond fieldtype 'DateField'}} 15 | {{> datefield}} 16 | {{/ifCond}} 17 | {{#ifCond fieldtype 'TimeField'}} 18 | {{> timefield}} 19 | {{/ifCond}} 20 | {{#ifCond fieldtype 'LookupField'}} 21 | {{> lookupfield}} 22 | {{/ifCond}} 23 | {{#ifCond fieldtype 'MultipleLookupField'}} 24 | {{> multiplelookupfield}} 25 | {{/ifCond}} 26 |
27 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/fields.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{> created-field}} 3 |
4 | {{#each fields}} 5 |
6 | {{#ifCond fieldtype 'TextField'}} 7 | {{> textfield}} 8 | {{/ifCond}} 9 | {{#ifCond fieldtype 'NumericField'}} 10 | {{> numericfield}} 11 | {{/ifCond}} 12 | {{#ifCond fieldtype 'DateTimeField'}} 13 | {{> datetimefield}} 14 | {{/ifCond}} 15 | {{#ifCond fieldtype 'DateField'}} 16 | {{> datefield}} 17 | {{/ifCond}} 18 | {{#ifCond fieldtype 'TimeField'}} 19 | {{> timefield}} 20 | {{/ifCond}} 21 | {{#ifCond fieldtype 'LookupField'}} 22 | {{> lookupfield}} 23 | {{/ifCond}} 24 | {{#ifCond fieldtype 'MultipleLookupField'}} 25 | {{> multiplelookupfield}} 26 | {{/ifCond}} 27 |
28 | {{/each}} 29 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/fieldselect.hbs: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 | -------------------------------------------------------------------------------- /geokey/templates/handlebars/helpers.js: -------------------------------------------------------------------------------- 1 | Handlebars.registerHelper('value', function(feature, field) { 2 | var value = (feature.properties[field.key] ? feature.properties[field.key] : '—'); 3 | 4 | switch (field.fieldtype) { 5 | case 'LookupField': 6 | for (var j = 0, lenj = field.lookupvalues.length; j < lenj; j++) { 7 | if (field.lookupvalues[j].id === value) { 8 | value = field.lookupvalues[j].name; 9 | break; 10 | } 11 | } 12 | break; 13 | case 'DateTimeField': 14 | value = moment(value).fromNow() + ' ('+ moment(value).format('llll') +')'; 15 | break; 16 | } 17 | return value; 18 | }); 19 | 20 | Handlebars.registerHelper('ifCond', function(v1, v2, options) { 21 | if(v1 === v2) { 22 | return options.fn(this); 23 | } 24 | return options.inverse(this); 25 | }); -------------------------------------------------------------------------------- /geokey/templates/handlebars/usergroupusers.hbs: -------------------------------------------------------------------------------- 1 | {{#each users}} 2 |
  • {{ display_name }} remove
  • 3 | {{/each}} -------------------------------------------------------------------------------- /geokey/templates/handlebars/userstypeaway.hbs: -------------------------------------------------------------------------------- 1 | {{#each this}} 2 |
  • {{ display_name }}
  • 3 | {{/each}} -------------------------------------------------------------------------------- /geokey/templates/oauth2_provider/authorize.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} | Authorise application{% endblock %} 4 | 5 | {% block main %} 6 | 11 | 12 |
    13 |
    14 |
    15 | {% csrf_token %} 16 |

    Authorise application {{ application.name }}

    17 | 18 | {% for field in form %} 19 | {% if field.is_hidden %} 20 | {{ field }} 21 | {% endif %} 22 | {% endfor %} 23 | 24 |

    {{ application.name }} would like to access your data with the following permissions:

    25 | 26 |
      27 | {% for scopes_description in scopes_descriptions %} 28 |
    • {{ scopes_description }}
    • 29 | {% endfor %} 30 |
    31 | 32 |
    33 | 34 | 35 |
    36 |
    37 |
    38 |
    39 | {% endblock %} 40 | -------------------------------------------------------------------------------- /geokey/templates/projects/navigation.html: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /geokey/templates/projects/project_attributes.html: -------------------------------------------------------------------------------- 1 |

    2 | {{ private_label }} 3 | {% if inactive %}Archived{% endif %} 4 |

    5 | 6 |

    Created by {{ creator }} on {{ created_at }}

    7 | -------------------------------------------------------------------------------- /geokey/templates/projects/projects_involved.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} | Other projects{% endblock %} 4 | 5 | {% block main %} 6 | 11 | 12 |
    13 | {% include 'snippets/messages.html' %} 14 | 15 | {% for project in projects %} 16 |
    17 |
    18 |

    {{ project.name }}

    19 |

    {{ project.role }}

    20 |
    21 | 22 |
    23 | {{ project.contributions }} 24 | contributions 25 |
    26 |
    27 | {% empty %} 28 |
    29 |
    30 |

    No projects found.

    31 |
    32 |
    33 | {% endfor %} 34 |
    35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /geokey/templates/snippets/error.html: -------------------------------------------------------------------------------- 1 | 10 | 11 |
    12 |
    13 |
    14 |
    {{ error_description }}
    15 |
    16 |
    17 |
    18 | -------------------------------------------------------------------------------- /geokey/templates/snippets/footer.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /geokey/templates/snippets/google_analytics.html: -------------------------------------------------------------------------------- 1 | {% if GOOGLE_ANALYTICS %} 2 | 11 | {% endif %} 12 | -------------------------------------------------------------------------------- /geokey/templates/snippets/messages.html: -------------------------------------------------------------------------------- 1 | {% if DEBUG %} 2 |
    3 |

    The administrator has enabled DEBUG mode for this platform. This very likely means that the platform is run as a test environment and things may break. Please get in touch with the administrator if you constantly experience problems.

    4 |
    5 | {% endif %} 6 | 7 | {% for message in messages %} 8 |
    9 | 10 | {{ message|linebreaks }} 11 |
    12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /geokey/templates/snippets/project_help.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /geokey/templates/snippets/social_apps.html: -------------------------------------------------------------------------------- 1 | {% load socialaccount %} 2 | 3 | {% if social_apps %} 4 | 11 | {% endif %} 12 | -------------------------------------------------------------------------------- /geokey/templates/snippets/usergroup_editor.html: -------------------------------------------------------------------------------- 1 |

    2 | {% if project.islocked %}{% endif %} 3 | User group members 4 |

    5 | 6 | {% if project.islocked %} 7 |
    8 |

    The project is locked, therefore user group cannot be edited. If you wish to edit it, you have to unlock the project in the project settings first.

    9 |
    10 | {% else %} 11 | 18 | {% endif %} 19 | 20 | {% if users.all %} 21 | 33 | {% else %} 34 |
    35 |

    We couldn't find any users assigned to this user group.

    36 | 37 | {% if project.islocked %} 38 |

    Unfortunately, you cannot add new users when the project is locked.

    39 | {% else %} 40 |

    Type a display name in the input field to find the user you want to add, then click on the name in the results list to add the user to this user group.

    41 | {% endif %} 42 |
    43 | {% endif %} 44 | -------------------------------------------------------------------------------- /geokey/templates/socialaccount/authentication_error.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Authentication error{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
    12 |
    13 |
    14 |
    An error occurred while attempting to authenticate you. Please try to sign in again.
    15 |
    16 |
    17 |
    18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/socialaccount/login_cancelled.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block title %} | Authentication cancelled{% endblock %} 3 | 4 | {% block main %} 5 | 10 | 11 |
    12 |
    13 |
    14 |
    You decided to cancel the authentication process using one of your existing accounts. If this was a mistake, please proceed to sign in page.
    15 |
    16 |
    17 |
    18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /geokey/templates/subsets/subset_navigation.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    Subset
    4 | 5 |

    6 | {% if project.islocked %}{% endif %} 7 | {{ subset.name }} 8 |

    9 |
    10 | 11 |
    12 | Data 13 | Settings 14 |
    15 |
    16 | -------------------------------------------------------------------------------- /geokey/templates/superusertools/navigation.html: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /geokey/templates/superusertools/platform_settings.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} | Superuser tools - Platform settings{% endblock %} 4 | 5 | {% block main %} 6 | 12 | 13 |
    14 | {% include 'snippets/messages.html' %} 15 | 16 |
    17 |
    18 |
    19 | {% csrf_token %} 20 | 21 |

    General settings

    22 | 23 |
    24 | 25 | 26 |
    27 | 28 |
    29 | 30 | 31 |
    32 | 33 |
    34 | 35 | 36 |
    37 |
    38 |
    39 |
    40 |
    41 | {% endblock %} 42 | 43 | {% block libraries %} 44 | 45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /geokey/templates/superusertools/provider_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} | Superuser tools - Providers{% endblock %} 4 | 5 | {% block main %} 6 | 12 | 13 |
    14 | {% include 'snippets/messages.html' %} 15 | 16 |
    17 |
    18 |
    19 |

    Providers allow users to connect their social accounts and sign in to the {{ PLATFORM_NAME }} using them.

    20 |

    In order to enable a provider, the application must be registered on its API tools to retrieve the required credentials.

    21 |
    22 |
    23 | 24 |
    25 |

    Providers

    26 | 27 | {% for provider in providers %} 28 | {% if forloop.first %} 29 | 38 | {% endif %} 39 | {% empty %} 40 |
    41 |

    There are no providers available.

    42 |
    43 | {% endfor %} 44 |
    45 |
    46 |
    47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /geokey/templates/superusertools/provider_overview.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %} | Superuser tools - Provider: {{ provider.name }}{% endblock %} 4 | 5 | {% block main %} 6 | 12 | 13 |
    14 | {% include 'snippets/messages.html' %} 15 | 16 |
    17 |
    18 | {% csrf_token %} 19 | 20 |

    {{ provider.name }} provider

    21 | 22 |
    23 | 24 | 25 |
    26 | 27 |
    28 | 29 | 30 |
    31 | 32 |
    33 | 34 | 35 |
    36 | 37 |
    38 | 39 | Cancel 40 |
    41 |
    42 |
    43 |
    44 | {% endblock %} 45 | 46 | {% block libraries %} 47 | 48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /geokey/templates/users/usergroup_navigation.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    User group
    4 | 5 |

    6 | {% if project.islocked %}{% endif %} 7 | {{ usergroup.name }} 8 |

    9 |
    10 | 11 |
    12 | Members 13 | Data 14 | Permissions 15 | Settings 16 |
    17 |
    18 | -------------------------------------------------------------------------------- /geokey/templates/users/usergroup_overview.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block bodydata %} 4 | data-project-id="{{ project.id }}" 5 | data-project-locked="{{ project.islocked }}" 6 | data-usergroup-id="{{ usergroup.id }}" 7 | {% endblock %} 8 | 9 | {% block title %} | Project: {{ project.name }} - User group: {{ usergroup.name }} - Overview{% endblock %} 10 | 11 | {% block main %} 12 | 22 | 23 |
    24 | {% include 'snippets/messages.html' %} 25 | 26 |
    27 |
    28 | {% include 'users/usergroup_navigation.html' %} 29 |
    30 | 31 |
    32 | {% include 'snippets/usergroup_editor.html' with users=usergroup.users.all %} 33 |
    34 |
    35 |
    36 | {% endblock %} 37 | 38 | {% block libraries %} 39 | 40 | 41 | 42 | 43 | 44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /geokey/users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/users/__init__.py -------------------------------------------------------------------------------- /geokey/users/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/users/management/__init__.py -------------------------------------------------------------------------------- /geokey/users/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/users/management/commands/__init__.py -------------------------------------------------------------------------------- /geokey/users/management/commands/check_confirm.py: -------------------------------------------------------------------------------- 1 | """Command `check_confirm`.""" 2 | 3 | from pytz import utc 4 | from datetime import datetime, timedelta 5 | 6 | from django.core.management.base import BaseCommand 7 | 8 | from allauth.account import app_settings 9 | from allauth.account.models import EmailAddress, EmailConfirmation 10 | 11 | 12 | class Command(BaseCommand): 13 | """A command to check which users should be inactivated.""" 14 | 15 | def set_user_inactive(self, yesterday): 16 | outstanding = EmailAddress.objects.filter( 17 | verified=False, 18 | user__is_active=True, 19 | user__is_superuser=False) 20 | 21 | for address in outstanding: 22 | try: 23 | confirmation = EmailConfirmation.objects.filter( 24 | email_address=address).latest('id') 25 | 26 | if confirmation.sent < yesterday: 27 | address.user.is_active = False 28 | address.user.save() 29 | except EmailConfirmation.DoesNotExist: 30 | if app_settings.EMAIL_VERIFICATION in ["optional", "required"]: 31 | address.user.is_active = False 32 | address.user.save() 33 | 34 | def handle(self, *args, **options): 35 | now = datetime.utcnow() - timedelta(1) 36 | yesterday = datetime( 37 | now.year, now.month, now.day, 0, 0, 0).replace(tzinfo=utc) 38 | 39 | self.set_user_inactive(yesterday) 40 | -------------------------------------------------------------------------------- /geokey/users/migrations/0002_auto_20150106_1420.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | 6 | 7 | def create_anonymous(apps, schema_editor): 8 | User = apps.get_model("users", "User") 9 | if not User.objects.filter(display_name='AnonymousUser').exists(): 10 | User.objects.create( 11 | display_name='AnonymousUser', 12 | password='', 13 | email='' 14 | ) 15 | 16 | 17 | class Migration(migrations.Migration): 18 | 19 | dependencies = [ 20 | ('users', '0001_initial'), 21 | ] 22 | 23 | operations = [ 24 | migrations.RunPython(create_anonymous), 25 | ] 26 | -------------------------------------------------------------------------------- /geokey/users/migrations/0002_auto_20150824_1603.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='user', 16 | name='email', 17 | field=models.EmailField(unique=True, max_length=254), 18 | ), 19 | migrations.AlterField( 20 | model_name='user', 21 | name='last_login', 22 | field=models.DateTimeField(null=True, verbose_name='last login', blank=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /geokey/users/migrations/0002_auto_20150904_1113.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='user', 16 | name='display_name', 17 | field=models.CharField(unique=True, max_length=50, error_messages={b'unique': b'A user is already registered with this display name.'}), 18 | preserve_default=True, 19 | ), 20 | migrations.AlterField( 21 | model_name='user', 22 | name='email', 23 | field=models.EmailField(unique=True, max_length=75, error_messages={b'unique': b'A user is already registered with this email address.'}), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /geokey/users/migrations/0003_auto_20150611_1307.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | try: 6 | from django.contrib.postgres.fields import JSONField 7 | except ImportError: 8 | from django_pgjson.fields import JsonBField as JSONField 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | ('users', '0002_auto_20150106_1420'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='usergroup', 20 | name='filters', 21 | field=JSONField(null=True, blank=True), 22 | preserve_default=True, 23 | ), 24 | migrations.AddField( 25 | model_name='usergroup', 26 | name='where_clause', 27 | field=models.TextField(null=True, blank=True), 28 | preserve_default=True, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /geokey/users/migrations/0004_auto_20150617_0902.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0003_auto_20150611_1307'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL('DROP TABLE IF EXISTS users_groupingusergroup;') 15 | ] 16 | -------------------------------------------------------------------------------- /geokey/users/migrations/0005_auto_20150825_0933.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0002_auto_20150824_1603'), 11 | ('oauth2_provider', '0002_08_updates'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RunSQL('DROP TABLE IF EXISTS oauth2_provider_application;') 16 | ] 17 | -------------------------------------------------------------------------------- /geokey/users/migrations/0006_merge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0002_auto_20150904_1113'), 11 | ('users', '0005_auto_20150825_0933'), 12 | ] 13 | 14 | operations = [ 15 | ] 16 | -------------------------------------------------------------------------------- /geokey/users/migrations/0007_auto_20151006_1110.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0006_merge'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='user', 16 | name='email', 17 | field=models.EmailField(unique=True, max_length=254, error_messages={b'unique': b'A user is already registered with this email address.'}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /geokey/users/migrations/0008_historicalusergroup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | try: 6 | from django.contrib.postgres.fields import JSONField 7 | except ImportError: 8 | from django_pgjson.fields import JsonBField as JSONField 9 | import django.db.models.deletion 10 | from django.conf import settings 11 | 12 | 13 | class Migration(migrations.Migration): 14 | 15 | dependencies = [ 16 | ('projects', '0008_historicalproject'), 17 | ('users', '0007_auto_20151006_1110'), 18 | ('users', '0004_auto_20150617_0902'), 19 | ] 20 | 21 | operations = [ 22 | migrations.CreateModel( 23 | name='HistoricalUserGroup', 24 | fields=[ 25 | ('id', models.IntegerField(verbose_name='ID', db_index=True, auto_created=True, blank=True)), 26 | ('name', models.CharField(max_length=100)), 27 | ('description', models.TextField(null=True, blank=True)), 28 | ('can_contribute', models.BooleanField(default=True)), 29 | ('can_moderate', models.BooleanField(default=False)), 30 | ('filters', JSONField(null=True, blank=True)), 31 | ('where_clause', models.TextField(null=True, blank=True)), 32 | ('history_id', models.AutoField(serialize=False, primary_key=True)), 33 | ('history_date', models.DateTimeField()), 34 | ('history_type', models.CharField(max_length=1, choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')])), 35 | ('history_user', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)), 36 | ('project', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.DO_NOTHING, db_constraint=False, blank=True, to='projects.Project', null=True)), 37 | ], 38 | options={ 39 | 'ordering': ('-history_date', '-history_id'), 40 | 'get_latest_by': 'history_date', 41 | 'verbose_name': 'historical user group', 42 | }, 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /geokey/users/migrations/0009_auto_20180502_1258.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0008_historicalusergroup'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='user', 16 | name='last_login', 17 | field=models.DateTimeField(null=True, verbose_name='last login', blank=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /geokey/users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/users/migrations/__init__.py -------------------------------------------------------------------------------- /geokey/users/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/users/templatetags/__init__.py -------------------------------------------------------------------------------- /geokey/users/templatetags/filter_tags.py: -------------------------------------------------------------------------------- 1 | """Template tags for filtering tags.""" 2 | 3 | from django import template 4 | from django.utils.safestring import mark_safe 5 | 6 | from geokey.categories.models import Field 7 | 8 | register = template.Library() 9 | 10 | 11 | @register.simple_tag 12 | def is_selected(value, arr): 13 | if str(value) in arr: 14 | return 'selected' 15 | return '' 16 | 17 | 18 | @register.simple_tag 19 | def show_restrict(rules, category): 20 | if rules: 21 | if str(category.id) in rules: 22 | if rules[str(category.id)] == {}: 23 | return mark_safe('Restrict further') 25 | 26 | return '' 27 | 28 | 29 | @register.filter(name='is_in') 30 | def is_in(d, key_name): 31 | if d is not None: 32 | if str(key_name) in d: 33 | return True 34 | 35 | return False 36 | 37 | 38 | @register.inclusion_tag('snippets/data_fields_rules.html') 39 | def show_fields(filters, category): 40 | if filters and str(category.id) in filters: 41 | cat_rules = filters.get(str(category.id)) 42 | 43 | context = { 44 | 'locked': category.project.islocked, 45 | 'min_date': cat_rules.pop('min_date', None), 46 | 'max_date': cat_rules.pop('max_date', None), 47 | 'fields': [], 48 | } 49 | 50 | for key in cat_rules: 51 | try: 52 | context['fields'].append({ 53 | 'category_id': category.id, 54 | 'field': category.fields.get_subclass(key=key), 55 | 'rule': cat_rules[key], 56 | }) 57 | except Field.DoesNotExist: 58 | pass 59 | 60 | return context 61 | 62 | 63 | @register.filter(name='minval') 64 | def minval(d): 65 | if d is not None: 66 | if d.get('minval'): 67 | return d.get('minval') 68 | 69 | return '' 70 | 71 | 72 | @register.filter(name='maxval') 73 | def maxval(d): 74 | if d is not None: 75 | if d.get('maxval'): 76 | return d.get('maxval') 77 | 78 | return '' 79 | -------------------------------------------------------------------------------- /geokey/users/templatetags/social.py: -------------------------------------------------------------------------------- 1 | """Template tags for social features.""" 2 | 3 | from django import template 4 | from django.db.models import Q 5 | 6 | from allauth.socialaccount import providers 7 | from allauth.socialaccount.models import SocialApp 8 | 9 | 10 | register = template.Library() 11 | 12 | 13 | @register.assignment_tag 14 | def get_social_apps(): 15 | """Get all enabled social apps.""" 16 | social_apps = SocialApp.objects.exclude(Q(client_id__exact='')).distinct() 17 | 18 | for social_app in social_apps: 19 | try: 20 | provider = providers.registry.by_id(social_app.provider) 21 | except: 22 | provider = None 23 | 24 | social_app.provider = provider 25 | 26 | return social_apps 27 | -------------------------------------------------------------------------------- /geokey/users/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/geokey/users/tests/__init__.py -------------------------------------------------------------------------------- /geokey/users/tests/test_serializers.py: -------------------------------------------------------------------------------- 1 | """Tests for serializers of users.""" 2 | 3 | from django.test import TestCase 4 | 5 | from rest_framework.serializers import ValidationError 6 | from allauth.socialaccount.models import SocialAccount 7 | 8 | from .model_factories import UserFactory 9 | from ..serializers import UserSerializer 10 | 11 | 12 | class UserSerializerTest(TestCase): 13 | 14 | def test_get_social_accounts_when_empty(self): 15 | user = UserFactory.create() 16 | serializer = UserSerializer(user) 17 | self.assertEqual(serializer.get_social_accounts(user), []) 18 | 19 | def test_get_social_accounts_when_connected_to_facebook(self): 20 | user = UserFactory.create() 21 | social_account = SocialAccount.objects.create( 22 | user=user, provider='facebook', uid='123') 23 | serializer = UserSerializer(user) 24 | self.assertEqual( 25 | serializer.get_social_accounts(user), 26 | [{ 27 | 'id': social_account.id, 28 | 'provider': 'facebook' 29 | }] 30 | ) 31 | 32 | def test_validate_display_name(self): 33 | UserFactory.create(**{'display_name': 'name'}) 34 | user = UserFactory.create() 35 | serializer = UserSerializer(user) 36 | 37 | try: 38 | serializer.validate_display_name('name') 39 | except ValidationError: 40 | pass 41 | else: 42 | self.fail('validate_display_name did not raise ValidationError') 43 | 44 | def test_validate_email(self): 45 | UserFactory.create(**{'email': 'name@example.com'}) 46 | user = UserFactory.create() 47 | serializer = UserSerializer(user) 48 | 49 | try: 50 | serializer.validate_email('name@example.com') 51 | except ValidationError: 52 | pass 53 | else: 54 | self.fail('validate_email did not raise ValidationError') 55 | -------------------------------------------------------------------------------- /geokey/users/tests/test_templatetags.py: -------------------------------------------------------------------------------- 1 | """Tests for template tags of users.""" 2 | 3 | from django.test import TestCase 4 | 5 | from geokey.categories.tests.model_factories import CategoryFactory 6 | 7 | from ..templatetags import filter_tags 8 | 9 | 10 | class TemplateTagsTest(TestCase): 11 | def test_show_restrict(self): 12 | category = CategoryFactory.create() 13 | self.assertEqual( 14 | filter_tags.show_restrict({str(category.id): {}}, category), 15 | '' 16 | 'Restrict further' 17 | ) 18 | self.assertEqual( 19 | filter_tags.show_restrict({'2': {}}, category), 20 | '' 21 | ) 22 | 23 | def test_is_selected(self): 24 | dict = ["1", "2", "3"] 25 | 26 | self.assertEqual(filter_tags.is_selected(1, dict), 'selected') 27 | self.assertEqual(filter_tags.is_selected(4, dict), '') 28 | 29 | def test_is_in(self): 30 | dict = { 31 | '1': {}, 32 | '2': {} 33 | } 34 | 35 | self.assertTrue(filter_tags.is_in(dict, 1)) 36 | self.assertFalse(filter_tags.is_in(dict, 4)) 37 | 38 | def test_minval(self): 39 | self.assertEqual(filter_tags.minval({'minval': 5}), 5) 40 | self.assertEqual(filter_tags.minval({}), '') 41 | 42 | def test_maxval(self): 43 | self.assertEqual(filter_tags.maxval({'maxval': 5}), 5) 44 | self.assertEqual(filter_tags.maxval({}), '') 45 | -------------------------------------------------------------------------------- /geokey/version.py: -------------------------------------------------------------------------------- 1 | """Version of GeoKey.""" 2 | 3 | VERSION = (1, 11, 2, 'final', 0) 4 | 5 | 6 | def get_version(): 7 | """ 8 | Return current version of GeoKey. 9 | 10 | Returns 11 | ------- 12 | str 13 | Current version. 14 | """ 15 | version = '%s.%s' % (VERSION[0], VERSION[1]) 16 | if VERSION[2]: 17 | version = '%s.%s' % (version, VERSION[2]) 18 | 19 | sub = '' 20 | if VERSION[3] != 'final': 21 | mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'} 22 | sub = mapping[VERSION[3]] + str(VERSION[4]) 23 | 24 | return version + sub 25 | -------------------------------------------------------------------------------- /local_settings.example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExCiteS/geokey/53b0bf85ffd30c9e958f028bf9ece5f6ef8d600f/local_settings.example/__init__.py -------------------------------------------------------------------------------- /local_settings.example/wsgi.py: -------------------------------------------------------------------------------- 1 | """WSGI configuration.""" 2 | 3 | import os 4 | 5 | from django.core.wsgi import get_wsgi_application 6 | 7 | 8 | try: 9 | import local_settings 10 | settings_module = 'settings' 11 | except ImportError: 12 | settings_module = 'geokey.core.settings.project' 13 | 14 | 15 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module) 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Default Django manager, when using local settings.""" 3 | 4 | import os 5 | import sys 6 | 7 | 8 | if __name__ == '__main__': 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'local_settings.settings') 10 | from django.core.management import execute_from_command_line 11 | execute_from_command_line(sys.argv) 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GeoKey", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "grunt": "*", 6 | "grunt-contrib-concat": "*", 7 | "grunt-contrib-handlebars": "*", 8 | "grunt-contrib-uglify": "*", 9 | "grunt-contrib-watch": "*" 10 | }, 11 | "scripts": { 12 | "grunt": "grunt" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | factory-boy==2.11.1 2 | django-debug-toolbar==1.7 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django>=1.8.19,<1.12 2 | django-aggregate-if==0.5 3 | django-allauth==0.33.0 4 | django-braces==1.11.0 5 | django-crontab==0.7.1 6 | django-hstore==1.4.2 7 | django-model-utils==3.1.2 8 | django-oauth-toolkit==0.12.0 9 | django-pgjson==0.3.1 10 | django-simple-history==1.8.2 11 | django-nose==1.4.5 12 | djangorestframework==3.9.4 13 | djangorestframework-gis==0.14.0 14 | easy-thumbnails==2.4.2 15 | gdata==2.0.18 16 | gdal==1.10.0 17 | iso8601==0.1.12 18 | moment==0.8.2 19 | nose==1.3.7 20 | pillow==6.2.0 21 | psycopg2==2.7.3.1 22 | pytz==2017.2 23 | python-magic==0.4.15 24 | requests[security]==2.20.0 25 | tweepy==3.5.0 26 | facebook-sdk==2.0.0 27 | oauth2client==4.1.2 28 | oauthlib==2.0.6 29 | google-api-python-client==1.6.4 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """GeoKey setup.""" 4 | 5 | from os.path import join 6 | from setuptools import setup, find_packages 7 | 8 | from geokey.version import get_version 9 | 10 | 11 | name = 'geokey' 12 | version = get_version() 13 | repository = join('https://github.com/ExCiteS', name) 14 | 15 | 16 | def get_install_requires(): 17 | """ 18 | Get requirements (ignore links, exclude comments). 19 | 20 | Returns 21 | ------- 22 | list 23 | Requirements for GeoKey. 24 | """ 25 | requirements = list() 26 | for line in open('requirements.txt').readlines(): 27 | if line.startswith('#') or line.startswith('git+https') or line == '': 28 | continue 29 | requirements.append(line.rstrip()) 30 | return requirements 31 | 32 | 33 | setup( 34 | name=name, 35 | version=version, 36 | description='Platform for participatory mapping', 37 | url='http://geokey.org.uk', 38 | download_url=join(repository, 'tarball', version), 39 | author='ExCiteS', 40 | author_email='excites@ucl.ac.uk', 41 | license='Apache 2.0', 42 | packages=find_packages(), 43 | include_package_data=True, 44 | install_requires=get_install_requires(), 45 | ) 46 | --------------------------------------------------------------------------------