From a205eff485ad373e0489df5e37be4520c113834c Mon Sep 17 00:00:00 2001
From: Fabian Binder
Date: Thu, 8 Sep 2022 10:16:14 +0200
Subject: [PATCH] Autoformat on pre-commit (#259)
* Add pre-commit lint hook for local dev
* Add general formatting to pre-commit-config
* Add autoremove unused variables to pre-commit-config
* Add black formatter to pre-commit-config
* Apply pre-commit formatters on all files
Co-authored-by: Dimas Ciputra
---
.flake8 | 2 +-
.pre-commit-config.yaml | 35 +
COPYING | 10 +-
INSTALL.md | 12 +-
Makefile | 2 +-
REQUIREMENTS.txt | 2 +-
apache/apache.virtenv.conf.example | 4 +-
apache/django.wsgi | 14 +-
createdb.sh | 1 -
doc/APPS.t2t | 3 +-
dockerize/plugins_mirror.sh | 2 +-
qgis-app/REQUIREMENTS_plugins.txt | 2 +-
qgis-app/api/apps.py | 2 +-
qgis-app/api/permissions.py | 2 +-
qgis-app/api/serializers.py | 37 +-
qgis-app/api/tests/test_views.py | 141 ++-
qgis-app/api/urls.py | 11 +-
qgis-app/api/views.py | 94 +-
qgis-app/base/forms/processing_forms.py | 41 +-
qgis-app/base/license.py | 2 +-
qgis-app/base/models/processing_models.py | 119 ++-
qgis-app/base/models/site_preferences.py | 8 +-
qgis-app/base/tests/test_license.py | 10 +-
qgis-app/base/tests/testfiles/test.txt | 2 +-
qgis-app/base/validator.py | 6 +-
qgis-app/base/views/processing_view.py | 385 ++++----
qgis-app/custom_haystack_urls.py | 22 +-
qgis-app/fixtures/auth.json | 84 +-
qgis-app/fixtures/plugins.json | 2 +-
qgis-app/fixtures/simplemenu.json | 2 +-
qgis-app/fixtures/styles.json | 2 +-
qgis-app/geopackages/admin.py | 25 +-
qgis-app/geopackages/apps.py | 2 +-
qgis-app/geopackages/forms.py | 11 +-
.../geopackages/migrations/0001_initial.py | 172 +++-
.../migrations/0002_auto_20201215_2123.py | 117 ++-
...0003_rename_Review_model_and_file_field.py | 22 +-
.../migrations/0004_auto_20210121_0227.py | 16 +-
.../migrations/0005_auto_20210203_2142.py | 16 +-
.../migrations/0006_add_uuid_field.py | 9 +-
.../migrations/0007_populate_uuid_value.py | 9 +-
.../migrations/0008_remove_uuid_null.py | 9 +-
qgis-app/geopackages/models.py | 34 +-
.../geopackages/2020/spiky_polygons.gpkg | 2 +-
qgis-app/geopackages/tests/test_views.py | 168 ++--
qgis-app/geopackages/urls.py | 65 +-
qgis-app/geopackages/views.py | 37 +-
qgis-app/homepage.py | 26 +-
qgis-app/layerdefinitions/admin.py | 22 +-
qgis-app/layerdefinitions/apps.py | 2 +-
qgis-app/layerdefinitions/file_handler.py | 16 +-
qgis-app/layerdefinitions/forms.py | 15 +-
qgis-app/layerdefinitions/license.py | 6 +-
.../migrations/0001_initial.py | 215 ++++-
qgis-app/layerdefinitions/models.py | 62 +-
.../tests/test_file_handler.py | 63 +-
qgis-app/layerdefinitions/tests/test_forms.py | 7 +-
qgis-app/layerdefinitions/tests/test_views.py | 173 ++--
qgis-app/layerdefinitions/urls.py | 83 +-
qgis-app/layerdefinitions/views.py | 86 +-
qgis-app/lib/templatetags/avatar_exists.py | 9 +-
qgis-app/lib/templatetags/sort_anchor.py | 48 +-
qgis-app/middleware.py | 2 +-
qgis-app/models/admin.py | 25 +-
qgis-app/models/apps.py | 2 +-
qgis-app/models/forms.py | 11 +-
qgis-app/models/migrations/0001_initial.py | 174 +++-
...0002_rename_Review_model_and_file_field.py | 39 +-
.../migrations/0003_auto_20210203_2142.py | 16 +-
.../models/migrations/0004_add_uuid_field.py | 9 +-
.../migrations/0005_populate_uuid_value.py | 9 +-
.../migrations/0006_remove_uuid_null.py | 9 +-
qgis-app/models/models.py | 39 +-
.../templatetags/resources_custom_tags.py | 29 +-
qgis-app/models/tests/test_views.py | 176 ++--
qgis-app/models/urls.py | 56 +-
qgis-app/models/validator.py | 22 +-
qgis-app/models/views.py | 36 +-
qgis-app/plugins/__init__.py | 14 +-
qgis-app/plugins/admin.py | 37 +-
qgis-app/plugins/api.py | 183 ++--
qgis-app/plugins/apps.py | 3 +-
qgis-app/plugins/celery.py | 10 +-
qgis-app/plugins/forms.py | 174 +++-
.../commands/generate_plugins_xml.py | 14 +-
qgis-app/plugins/middleware.py | 15 +-
qgis-app/plugins/migrations/0001_initial.py | 274 +++++-
qgis-app/plugins/models.py | 421 +++++---
qgis-app/plugins/search_indexes.py | 12 +-
qgis-app/plugins/tasks/__init__.py | 2 +-
.../plugins/tasks/generate_plugins_xml.py | 70 +-
qgis-app/plugins/templates/plugins/user.html | 1 -
.../plugins/version_permission_deny.html | 2 +-
.../plugins/templatetags/local_timezone.py | 2 +-
qgis-app/plugins/templatetags/plugin_utils.py | 17 +-
.../plugins/templatetags/plugins_tagcloud.py | 72 +-
qgis-app/plugins/templatetags/range_filter.py | 13 +-
.../plugins/templatetags/smart_paginate.py | 126 +--
.../1.0-spaced/Hello World/HelloWorld.py | 18 +-
.../1.0-spaced/Hello World/__init__.py | 23 +-
.../HelloWorld/1.0/HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/1.0/HelloWorld/__init__.py | 25 +-
.../HelloWorld/1.1/HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/1.1/HelloWorld/__init__.py | 25 +-
.../1.2-md-full/HelloWorld/HelloWorld.py | 18 +-
.../1.2-md-full/HelloWorld/__init__.py | 32 +-
.../1.2-md-full/HelloWorld/metadata.txt | 2 -
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 24 +-
.../HelloWorld/metadata.txt | 1 -
.../1.2-no-icon/HelloWorld/HelloWorld.py | 18 +-
.../1.2-no-icon/HelloWorld/__init__.py | 22 +-
.../1.2-no-icon/HelloWorld/metadata.txt | 2 -
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 25 +-
.../HelloWorld/metadata.txt | 1 -
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/metadata.txt | 1 -
.../1.2-wierdname/HelloWorld/HelloWorld.py | 18 +-
.../1.2-wierdname/HelloWorld/__init__.py | 25 +-
.../1.2-wierdname/HelloWorld/metadata.txt | 2 -
.../HelloWorld/1.2/HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/1.2/HelloWorld/__init__.py | 25 +-
.../HelloWorld/1.2/HelloWorld/metadata.txt | 2 -
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/metadata.txt | 2 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/metadata.txt | 2 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/metadata.txt | 2 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/metadata.txt | 2 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
.../HelloWorld/HelloWorld.py | 18 +-
.../HelloWorld/__init__.py | 6 +-
qgis-app/plugins/tests/__init__.py | 4 +-
.../plugins/tests/test_filter_template.py | 26 +-
qgis-app/plugins/tests/test_simple_tag.py | 48 +-
qgis-app/plugins/tests/test_validator.py | 156 ++-
qgis-app/plugins/tests/tests.py | 9 +-
qgis-app/plugins/tests/upload_test.py | 4 +-
qgis-app/plugins/tests/versionfield.py | 59 +-
qgis-app/plugins/urls.py | 309 ++++--
qgis-app/plugins/validator.py | 281 ++++--
qgis-app/plugins/views.py | 902 +++++++++++-------
qgis-app/qgis_context_processor.py | 14 +-
qgis-app/search_sites.py | 2 +-
qgis-app/settings.py | 321 +++----
qgis-app/settings_auth.py | 29 +-
qgis-app/settings_docker.py | 143 ++-
qgis-app/settings_local_vagrant.py | 168 ++--
qgis-app/styles/admin.py | 23 +-
qgis-app/styles/apps.py | 2 +-
qgis-app/styles/file_handler.py | 95 +-
qgis-app/styles/forms.py | 53 +-
qgis-app/styles/migrations/0001_initial.py | 179 +++-
.../migrations/0002_auto_20201108_0521.py | 53 +-
.../migrations/0003_stylereview_comment.py | 14 +-
.../migrations/0004_auto_20201108_0742.py | 6 +-
.../migrations/0005_style_modified_date.py | 14 +-
.../0006_stylereview_require_action.py | 13 +-
.../migrations/0007_auto_20201109_0112.py | 17 +-
.../migrations/0008_auto_20201215_2124.py | 69 +-
.../migrations/0009_auto_20210121_0227.py | 16 +-
...0010_rename_Review_model_and_file_field.py | 40 +-
.../migrations/0011_auto_20210203_2142.py | 29 +-
.../styles/migrations/0012_add_uuid_field.py | 9 +-
.../migrations/0013_populate_uuid_value.py | 9 +-
.../migrations/0014_remove_uuid_null.py | 9 +-
qgis-app/styles/models.py | 115 ++-
.../styles/templates/styles/style_detail.html | 2 +-
.../styles/templates/styles/style_form.html | 2 +-
.../styles/templates/styles/style_list.html | 1 -
.../styles/templates/styles/style_review.html | 2 +-
.../templates/styles/style_update_form.html | 2 +-
.../styles/templatetags/styles_custom_tags.py | 14 +-
qgis-app/styles/tests/test_filehandler.py | 122 +--
qgis-app/styles/tests/test_models.py | 71 +-
qgis-app/styles/tests/test_views.py | 250 ++---
qgis-app/styles/urls.py | 61 +-
qgis-app/styles/views.py | 89 +-
qgis-app/templates/404.html | 2 +-
qgis-app/templates/500.html | 1 -
.../templates/admin/auth/change_list.html | 1 -
qgis-app/templates/ajax_base.html | 2 +-
qgis-app/templates/base/base.html | 2 +-
qgis-app/templates/base/detail.html | 2 +-
qgis-app/templates/base/form_snippet.html | 2 +-
.../includes/layerdefinition/license.html | 2 +-
.../layerdefinition/review_datasource.html | 2 +-
qgis-app/templates/base/includes/license.html | 2 +-
.../includes/wavefront/detail_3dviewer.html | 2 +-
qgis-app/templates/base/list.html | 1 -
qgis-app/templates/base/list_galery.html | 1 -
qgis-app/templates/base/review.html | 2 +-
qgis-app/templates/base/update_form.html | 2 +-
qgis-app/templates/base/upload_form.html | 2 +-
.../templates/feedjack/default/post_list.html | 1 -
qgis-app/templates/feedjack/post.html | 1 -
qgis-app/templates/flatpages/default.html | 2 +-
qgis-app/templates/index.html | 2 +-
qgis-app/templates/snippet_base.html | 1 -
qgis-app/templates/tinymce/NAME_textareas.js | 2 +-
qgis-app/urls.py | 96 +-
qgis-app/userexport/urls.py | 11 +-
qgis-app/userexport/views.py | 69 +-
qgis-app/users/admin.py | 12 +-
qgis-app/users/forms.py | 136 +--
qgis-app/users/models.py | 30 +-
qgis-app/users/templates/email_confirm.html | 1 -
qgis-app/users/templates/update_user.html | 2 -
qgis-app/users/templates/user_balloon.html | 2 -
qgis-app/users/templates/user_base.html | 1 -
qgis-app/users/templates/view_users.html | 2 -
qgis-app/users/tests.py | 9 +-
qgis-app/users/urls.py | 5 +-
qgis-app/users/views.py | 204 ++--
qgis-app/wavefronts/admin.py | 31 +-
qgis-app/wavefronts/apps.py | 2 +-
qgis-app/wavefronts/forms.py | 17 +-
.../wavefronts/migrations/0001_initial.py | 180 +++-
qgis-app/wavefronts/models.py | 38 +-
qgis-app/wavefronts/tests/test_validator.py | 43 +-
qgis-app/wavefronts/tests/test_view.py | 117 ++-
qgis-app/wavefronts/urls.py | 63 +-
qgis-app/wavefronts/utilities.py | 7 +-
qgis-app/wavefronts/validator.py | 40 +-
qgis-app/wavefronts/views.py | 76 +-
qgis-app/wsgi.py | 2 +
vagrant_assets/create_initial_data.sh | 1 -
vagrant_assets/settings_local_vagrant.py | 168 ++--
vagrant_assets/systemd/django.service | 2 +-
243 files changed, 6392 insertions(+), 4245 deletions(-)
diff --git a/.flake8 b/.flake8
index 03d9af5e..01572a25 100644
--- a/.flake8
+++ b/.flake8
@@ -19,4 +19,4 @@ max-line-length = 79
# E12x continuation line indentation
# E251 no spaces around keyword / parameter equals
# E303 too many blank lines (3)
-ignore = E121,E122,E123,E124,E125,E126,E127,E128,E251,E303,W503,W504,W60,F405
+ignore = E121,E122,E123,E124,E125,E126,E127,E128,E251,E303,W503,W504,W60,F405,E501
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 90682fed..77c5709e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,41 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
+
+exclude: 'static/'
repos:
+ # Fix end of files
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v3.2.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ args:
+ - '--fix=lf'
+
+ # Remove unused imports/variables
+ - repo: https://github.com/myint/autoflake
+ rev: v1.4
+ hooks:
+ - id: autoflake
+ args:
+ - "--in-place"
+ - "--remove-unused-variables"
+ # - "--remove-all-unused-imports"
+
+ # Sort imports
+ - repo: https://github.com/pycqa/isort
+ rev: "5.7.0"
+ hooks:
+ - id: isort
+ args: ["--profile", "black"]
+
+ # Black formatting
+ - repo: https://github.com/psf/black
+ rev: 20.8b1
+ hooks:
+ - id: black
+
# Lint files
- repo: https://gitlab.com/pycqa/flake8
rev: "3.9.0"
diff --git a/COPYING b/COPYING
index 6f9932ae..729b0741 100644
--- a/COPYING
+++ b/COPYING
@@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
-
+
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
-
+
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
-
+
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
-
+
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
-
+
In addition, as a special exception, the QGIS Development Team gives
permission to link the code of this program with the Qt library,
including but not limited to the following versions (both free and
diff --git a/INSTALL.md b/INSTALL.md
index 9d796ea5..1b607c93 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -120,7 +120,7 @@ Follow these steps for setting up:
- Create new Group: `Authentication and Authorization > Groups > Add group`
- Set the options:
- **Name:** `Style Managers`
- - **Permissions:**
+ - **Permissions:**
choose all styles | in **Available permissions** by typing `styles` in search input and click `Choose all`
- styles | style | Can add style
- styles | style | Can change style
@@ -134,7 +134,7 @@ Follow these steps for setting up:
- styles | style type | Can change style type
- styles | style type | Can delete style type
- styles | style type | Can view style type
-
+
---
## Backup and Restore
@@ -142,7 +142,7 @@ Follow these steps for setting up:
```bash
$ ./backup.sh
```
-- You will find dumps file in backups directory
+- You will find dumps file in backups directory
```bash
$ tree -L 3 backups
backups
@@ -174,7 +174,7 @@ backups
│`
```
-- Copy the dump file you wish to restore to dockerize/backups/latest.dmp file
+- Copy the dump file you wish to restore to dockerize/backups/latest.dmp file
```bash
$ cp backups/2020/December/PG_QGIS_PLUGINS_gis.16-December-2020.dmp dockerize/backups/latest.dmp
```
@@ -198,9 +198,9 @@ $ make dbrestore
- In Containers tab, click on tripe-dot icon > `Execute Shell`
![image](https://user-images.githubusercontent.com/40058076/102454128-7352ee00-4078-11eb-80aa-f782c6ea9f6f.png)
-
+
Now you are inside the container and ready to deploy your update.
-
+
![image](https://user-images.githubusercontent.com/40058076/102455185-1ce6af00-407a-11eb-8318-3e084b24c095.png)
- Pull the latest commit: `git pull origin master`
- Run migration: `python manage.py migrate`
diff --git a/Makefile b/Makefile
index 4d01a9d7..9030f068 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ all:
run: kill_server
- cd $(PRJ_DIR) && python manage.py runserver 0.0.0.0:8000
+ cd $(PRJ_DIR) && python manage.py runserver 0.0.0.0:8000
kill_server:
@if /usr/sbin/lsof -i :8000; then \
diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt
index b8847c5a..22a94caa 100644
--- a/REQUIREMENTS.txt
+++ b/REQUIREMENTS.txt
@@ -54,4 +54,4 @@ django-sortable-listview==0.43
django-user-map
djangorestframework==3.12.2
django-rest-auth==0.9.5
-drf-yasg
\ No newline at end of file
+drf-yasg
diff --git a/apache/apache.virtenv.conf.example b/apache/apache.virtenv.conf.example
index 2969d5ab..e92e25e5 100644
--- a/apache/apache.virtenv.conf.example
+++ b/apache/apache.virtenv.conf.example
@@ -1,12 +1,12 @@
- ServerAdmin tim@linfiniti.com
+ ServerAdmin tim@linfiniti.com
ServerName qgis-django.localhost
ServerAlias www.qgis-django.localhost
DocumentRoot /var/www
CustomLog /var/log/apache2/qgis-django.access.log combined
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
- LogLevel debug
+ LogLevel debug
#warn
ErrorLog /var/log/apache2/qgis-django.error.log
ServerSignature Off
diff --git a/apache/django.wsgi b/apache/django.wsgi
index e6aab523..9859c572 100644
--- a/apache/django.wsgi
+++ b/apache/django.wsgi
@@ -2,14 +2,14 @@ import os
import sys
ROOT_PROJECT_FOLDER = os.path.dirname(__file__)
-path1 = os.path.abspath(os.path.join(ROOT_PROJECT_FOLDER, '..'))
-path2 = os.path.abspath(os.path.join(ROOT_PROJECT_FOLDER, '..', 'qgis-app'))
+path1 = os.path.abspath(os.path.join(ROOT_PROJECT_FOLDER, ".."))
+path2 = os.path.abspath(os.path.join(ROOT_PROJECT_FOLDER, "..", "qgis-app"))
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+os.environ["DJANGO_SETTINGS_MODULE"] = "settings"
import django.core.handlers.wsgi
-application = django.core.handlers.wsgi.WSGIHandler()
-sys.path.append( path1 )
-sys.path.append( path2 )
-file("/tmp/log.txt","wt").write( str(sys.path) )
+application = django.core.handlers.wsgi.WSGIHandler()
+sys.path.append(path1)
+sys.path.append(path2)
+file("/tmp/log.txt", "wt").write(str(sys.path))
diff --git a/createdb.sh b/createdb.sh
index 3f364c42..b1ae4052 100644
--- a/createdb.sh
+++ b/createdb.sh
@@ -3,4 +3,3 @@ createdb qgis-django
createlang plpgsql qgis-django
psql qgis-django < /usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql
psql qgis-django < /usr/share/postgresql/8.4/contrib/postgis-1.5/spatial_ref_sys.sql
-
diff --git a/doc/APPS.t2t b/doc/APPS.t2t
index e9929053..566ad128 100644
--- a/doc/APPS.t2t
+++ b/doc/APPS.t2t
@@ -16,7 +16,7 @@ Tim Sutton 2010
%This document is in text2tags format. You can generate html, plain text and
%moinmoin formatted documentation by running txt2tags on this document. See the
%txt2tags home page for more details. Please insert manual line breaks in this
-%document as it makes diffing for changes much easier. To do this in vim
+%document as it makes diffing for changes much easier. To do this in vim
%automatically, select a section then issue (gq) command. Please dont
%apply vim formatting to the whole document as it screws up some formatting
%rather apply it selectively to paragraphs where needed.
@@ -46,4 +46,3 @@ Tim Sutton 2010
+ web links - a list of sites, articles etc. that feature QGIS prominantly
+ translate - a pootle instance for QGIS
+ user showcase - as simple way for users to highlight their use of QGIS without writing a full blown case study
-
diff --git a/dockerize/plugins_mirror.sh b/dockerize/plugins_mirror.sh
index d68efb36..0da24026 100644
--- a/dockerize/plugins_mirror.sh
+++ b/dockerize/plugins_mirror.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/bash
#First the database backups on the remote server
SOURCE=/mnt/HC_Volume_4113275
diff --git a/qgis-app/REQUIREMENTS_plugins.txt b/qgis-app/REQUIREMENTS_plugins.txt
index 0393e683..f7dca41a 100644
--- a/qgis-app/REQUIREMENTS_plugins.txt
+++ b/qgis-app/REQUIREMENTS_plugins.txt
@@ -22,4 +22,4 @@ sorl-thumbnail
django-extensions
django-debug-toolbar==1.11.1
whoosh
-django-haystack
\ No newline at end of file
+django-haystack
diff --git a/qgis-app/api/apps.py b/qgis-app/api/apps.py
index d87006dd..14b89a82 100644
--- a/qgis-app/api/apps.py
+++ b/qgis-app/api/apps.py
@@ -2,4 +2,4 @@
class ApiConfig(AppConfig):
- name = 'api'
+ name = "api"
diff --git a/qgis-app/api/permissions.py b/qgis-app/api/permissions.py
index 0ce045f8..58765bf5 100644
--- a/qgis-app/api/permissions.py
+++ b/qgis-app/api/permissions.py
@@ -1,6 +1,6 @@
from rest_framework import permissions
-MANAGER_GROUP = 'Style Managers'
+MANAGER_GROUP = "Style Managers"
class ReadOnly(permissions.BasePermission):
diff --git a/qgis-app/api/serializers.py b/qgis-app/api/serializers.py
index 0f44a183..cd37912f 100644
--- a/qgis-app/api/serializers.py
+++ b/qgis-app/api/serializers.py
@@ -1,42 +1,37 @@
-from rest_framework import serializers
-from sorl_thumbnail_serializer.fields import HyperlinkedSorlImageField
-
from base.validator import filesize_validator
-
from geopackages.models import Geopackage
from models.models import Model
+from rest_framework import serializers
+from sorl_thumbnail_serializer.fields import HyperlinkedSorlImageField
from styles.models import Style
class ResourceBaseSerializer(serializers.ModelSerializer):
- creator = serializers.ReadOnlyField(source='get_creator_name')
+ creator = serializers.ReadOnlyField(source="get_creator_name")
resource_type = serializers.SerializerMethodField()
resource_subtype = serializers.SerializerMethodField()
# A thumbnail image, sorl options and read-only
thumbnail = HyperlinkedSorlImageField(
- '128x128',
- options={"crop": "center"},
- source='thumbnail_image',
- read_only=True
+ "128x128", options={"crop": "center"}, source="thumbnail_image", read_only=True
)
class Meta:
fields = [
- 'resource_type',
- 'resource_subtype',
- 'uuid',
- 'name',
- 'creator',
- 'upload_date',
- 'download_count',
- 'description',
- 'file',
- 'thumbnail',
+ "resource_type",
+ "resource_subtype",
+ "uuid",
+ "name",
+ "creator",
+ "upload_date",
+ "download_count",
+ "description",
+ "file",
+ "thumbnail",
]
def validate(self, attrs):
- file = attrs.get('file')
+ file = attrs.get("file")
filesize_validator(file)
return attrs
@@ -61,7 +56,7 @@ def get_resource_subtype(self, obj):
class StyleSerializer(ResourceBaseSerializer):
- resource_subtype = serializers.ReadOnlyField(source='get_style_type')
+ resource_subtype = serializers.ReadOnlyField(source="get_style_type")
class Meta(ResourceBaseSerializer.Meta):
model = Style
diff --git a/qgis-app/api/tests/test_views.py b/qgis-app/api/tests/test_views.py
index f4825fd4..7910428a 100644
--- a/qgis-app/api/tests/test_views.py
+++ b/qgis-app/api/tests/test_views.py
@@ -2,8 +2,9 @@
import json
import zipfile
-from django.contrib.auth.models import User, Group
+from django.contrib.auth.models import Group, User
from django.core.files.base import ContentFile
+from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse
@@ -12,20 +13,19 @@
from models.models import Model
from styles.models import Style, StyleType
-from django.core.files.uploadedfile import SimpleUploadedFile
-
@override_settings(MEDIA_ROOT="api/tests")
class TestResourceAPIList(TestCase):
def setUp(self):
small_gif = (
- b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04'
- b'\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02'
- b'\x02\x4c\x01\x00\x3b'
+ b"\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04"
+ b"\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02"
+ b"\x02\x4c\x01\x00\x3b"
)
self.thumbnail = SimpleUploadedFile(
- 'small.gif', small_gif, content_type='image/gif')
- self.file = ContentFile('text', 'a_filename')
+ "small.gif", small_gif, content_type="image/gif"
+ )
+ self.file = ContentFile("text", "a_filename")
self.creator = User.objects.create(
username="creator", email="creator@email.com"
@@ -36,9 +36,7 @@ def setUp(self):
# set creator password to password
self.creator.set_password("password")
self.creator.save()
- self.staff = User.objects.create(
- username="staff", email="staff@email.com"
- )
+ self.staff = User.objects.create(username="staff", email="staff@email.com")
self.staff.set_password("password")
self.staff.save()
self.group = Group.objects.create(name="Style Managers")
@@ -47,7 +45,8 @@ def setUp(self):
symbol_type="marker",
name="Marker",
description="a marker for testing purpose",
- icon=self.thumbnail)
+ icon=self.thumbnail,
+ )
self.style = Style.objects.create(
name="style_zero",
description="a style for testing purpose",
@@ -55,7 +54,7 @@ def setUp(self):
thumbnail_image=self.thumbnail,
file=self.file,
style_type=style_type,
- approved=True
+ approved=True,
)
# create geopackage
Geopackage.objects.create(
@@ -64,7 +63,7 @@ def setUp(self):
description="A GeoPackage for testing purpose",
thumbnail_image=self.thumbnail,
file=self.file,
- approved=True
+ approved=True,
)
# create Model
Model.objects.create(
@@ -73,7 +72,7 @@ def setUp(self):
description="A Model for testing purpose",
thumbnail_image=self.thumbnail,
file=self.file,
- approved=True
+ approved=True,
)
def tearDown(self):
@@ -81,148 +80,146 @@ def tearDown(self):
def test_get_list_resources(self):
""" test results all resources """
- url = reverse('resource-list')
+ url = reverse("resource-list")
response = self.client.get(url)
json_parse = json.loads(response.content)
- self.assertEqual(json_parse['total'], 3)
- result = json_parse['results']
+ self.assertEqual(json_parse["total"], 3)
+ result = json_parse["results"]
for i, d in enumerate(result):
- if d['resource_type'] == 'Geopackage':
+ if d["resource_type"] == "Geopackage":
g_index = i
- elif d['resource_type'] == 'Model':
+ elif d["resource_type"] == "Model":
m_index = i
- elif d['resource_type'] == 'Style':
+ elif d["resource_type"] == "Style":
s_index = i
self.assertIsNotNone(g_index)
self.assertIsNotNone(m_index)
self.assertIsNotNone(s_index)
- self.assertEqual(result[g_index]['resource_type'], 'Geopackage')
- self.assertEqual(result[g_index]['resource_subtype'], None)
- self.assertEqual(result[g_index]['creator'], 'creator')
- self.assertEqual(result[m_index]['resource_type'], 'Model')
- self.assertEqual(result[m_index]['resource_subtype'], None)
- self.assertEqual(result[m_index]['creator'], 'creator 0')
- self.assertEqual(result[s_index]['resource_type'], 'Style')
- self.assertEqual(result[s_index]['resource_subtype'], 'Marker')
- self.assertEqual(result[s_index]['creator'], 'creator')
+ self.assertEqual(result[g_index]["resource_type"], "Geopackage")
+ self.assertEqual(result[g_index]["resource_subtype"], None)
+ self.assertEqual(result[g_index]["creator"], "creator")
+ self.assertEqual(result[m_index]["resource_type"], "Model")
+ self.assertEqual(result[m_index]["resource_subtype"], None)
+ self.assertEqual(result[m_index]["creator"], "creator 0")
+ self.assertEqual(result[s_index]["resource_type"], "Style")
+ self.assertEqual(result[s_index]["resource_subtype"], "Marker")
+ self.assertEqual(result[s_index]["creator"], "creator")
def test_get_list_resources_with_filter(self):
- param = ('resource_type=geopackage')
- url = '%s?%s' % (reverse('resource-list'), param)
+ param = "resource_type=geopackage"
+ url = "%s?%s" % (reverse("resource-list"), param)
response = self.client.get(url)
json_parse = json.loads(response.content)
- self.assertEqual(json_parse['total'], 1)
- result = json_parse['results']
+ self.assertEqual(json_parse["total"], 1)
+ result = json_parse["results"]
g_index = None
m_index = None
s_index = None
for i, d in enumerate(result):
- if d['resource_type'] == 'Geopackage':
+ if d["resource_type"] == "Geopackage":
g_index = i
- elif d['resource_type'] == 'Model':
+ elif d["resource_type"] == "Model":
m_index = i
- elif d['resource_type'] == 'Style':
+ elif d["resource_type"] == "Style":
s_index = i
self.assertIsNotNone(g_index)
self.assertIsNone(m_index)
self.assertIsNone(s_index)
def test_get_list_resources_with_filter_resource_type(self):
- param = ('resource_type=geopackage')
- url = '%s?%s' % (reverse('resource-list'), param)
+ param = "resource_type=geopackage"
+ url = "%s?%s" % (reverse("resource-list"), param)
response = self.client.get(url)
json_parse = json.loads(response.content)
- self.assertEqual(json_parse['total'], 1)
- result = json_parse['results']
+ self.assertEqual(json_parse["total"], 1)
+ result = json_parse["results"]
g_index = None
m_index = None
s_index = None
for i, d in enumerate(result):
- if d['resource_type'] == 'Geopackage':
+ if d["resource_type"] == "Geopackage":
g_index = i
- elif d['resource_type'] == 'Model':
+ elif d["resource_type"] == "Model":
m_index = i
- elif d['resource_type'] == 'Style':
+ elif d["resource_type"] == "Style":
s_index = i
self.assertIsNotNone(g_index)
self.assertIsNone(m_index)
self.assertIsNone(s_index)
def test_get_list_resources_with_filter_resource_subtype(self):
- param = ('resource_subtype=Marker')
- url = '%s?%s' % (reverse('resource-list'), param)
+ param = "resource_subtype=Marker"
+ url = "%s?%s" % (reverse("resource-list"), param)
response = self.client.get(url)
json_parse = json.loads(response.content)
- self.assertEqual(json_parse['total'], 1)
- result = json_parse['results']
+ self.assertEqual(json_parse["total"], 1)
+ result = json_parse["results"]
g_index = None
m_index = None
s_index = None
for i, d in enumerate(result):
- if d['resource_type'] == 'Geopackage':
+ if d["resource_type"] == "Geopackage":
g_index = i
- elif d['resource_type'] == 'Model':
+ elif d["resource_type"] == "Model":
m_index = i
- elif d['resource_type'] == 'Style':
+ elif d["resource_type"] == "Style":
s_index = i
self.assertIsNone(g_index)
self.assertIsNone(m_index)
self.assertIsNotNone(s_index)
def test_get_list_resources_with_filter_creator(self):
- param = ('creator=creator 0')
- url = '%s?%s' % (reverse('resource-list'), param)
+ param = "creator=creator 0"
+ url = "%s?%s" % (reverse("resource-list"), param)
response = self.client.get(url)
json_parse = json.loads(response.content)
- self.assertEqual(json_parse['total'], 1)
- result = json_parse['results']
+ self.assertEqual(json_parse["total"], 1)
+ result = json_parse["results"]
g_index = None
m_index = None
s_index = None
for i, d in enumerate(result):
- if d['resource_type'] == 'Geopackage':
+ if d["resource_type"] == "Geopackage":
g_index = i
- elif d['resource_type'] == 'Model':
+ elif d["resource_type"] == "Model":
m_index = i
- elif d['resource_type'] == 'Style':
+ elif d["resource_type"] == "Style":
s_index = i
self.assertIsNone(g_index)
self.assertIsNotNone(m_index)
self.assertIsNone(s_index)
def test_get_list_resources_with_filter_keyword(self):
- param = ('keyword=testing')
- url = '%s?%s' % (reverse('resource-list'), param)
+ param = "keyword=testing"
+ url = "%s?%s" % (reverse("resource-list"), param)
response = self.client.get(url)
json_parse = json.loads(response.content)
- self.assertEqual(json_parse['total'], 3)
- result = json_parse['results']
+ self.assertEqual(json_parse["total"], 3)
+ result = json_parse["results"]
g_index = None
m_index = None
s_index = None
for i, d in enumerate(result):
- if d['resource_type'] == 'Geopackage':
+ if d["resource_type"] == "Geopackage":
g_index = i
- elif d['resource_type'] == 'Model':
+ elif d["resource_type"] == "Model":
m_index = i
- elif d['resource_type'] == 'Style':
+ elif d["resource_type"] == "Style":
s_index = i
self.assertIsNotNone(g_index)
self.assertIsNotNone(m_index)
self.assertIsNotNone(s_index)
def test_download_resource_should_be_a_file_in_a_zip(self):
- url = reverse('resource-download',
- kwargs={'uuid': self.style.uuid})
+ url = reverse("resource-download", kwargs={"uuid": self.style.uuid})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEquals(
- response.get('Content-Disposition'),
- 'attachment; filename=style_zero.zip'
+ response.get("Content-Disposition"), "attachment; filename=style_zero.zip"
)
with io.BytesIO(response.content) as file:
- zip_file = zipfile.ZipFile(file, 'r')
+ zip_file = zipfile.ZipFile(file, "r")
self.assertIsNone(zip_file.testzip())
- self.assertIn('a_filename', zip_file.namelist()[0])
- self.assertNotIn('.zip', zip_file.namelist())
+ self.assertIn("a_filename", zip_file.namelist()[0])
+ self.assertNotIn(".zip", zip_file.namelist())
zip_file.close()
diff --git a/qgis-app/api/urls.py b/qgis-app/api/urls.py
index e9e27131..c2048c34 100644
--- a/qgis-app/api/urls.py
+++ b/qgis-app/api/urls.py
@@ -1,10 +1,9 @@
+from api.views import ResourceAPIDownload, ResourceAPIList
from django.urls import path
-from api.views import ResourceAPIList, ResourceAPIDownload
-
-
urlpatterns = [
- path('resources/', ResourceAPIList.as_view(), name='resource-list'),
- path('resource//', ResourceAPIDownload.as_view(),
- name='resource-download')
+ path("resources/", ResourceAPIList.as_view(), name="resource-list"),
+ path(
+ "resource//", ResourceAPIDownload.as_view(), name="resource-download"
+ ),
]
diff --git a/qgis-app/api/views.py b/qgis-app/api/views.py
index a9d52e35..5d392d6f 100644
--- a/qgis-app/api/views.py
+++ b/qgis-app/api/views.py
@@ -1,31 +1,25 @@
from collections import OrderedDict
+from api.serializers import GeopackageSerializer, ModelSerializer, StyleSerializer
+from base.license import zip_a_file_if_not_zipfile
from django.contrib.postgres.search import SearchVector
from django.http import Http404, HttpResponse
from django.utils.decorators import method_decorator
from django.utils.text import slugify
from django.views.decorators.cache import cache_page
-
-
-from rest_framework import filters, permissions
-from rest_framework.views import APIView
-from drf_multiple_model.views import FlatMultipleModelAPIView
from drf_multiple_model.pagination import MultipleModelLimitOffsetPagination
+from drf_multiple_model.views import FlatMultipleModelAPIView
# models
from geopackages.models import Geopackage
from models.models import Model
+from rest_framework import filters, permissions
+from rest_framework.views import APIView
from styles.models import Style
-from base.license import zip_a_file_if_not_zipfile
-
-from api.serializers import (GeopackageSerializer,
- ModelSerializer,
- StyleSerializer)
-
def filter_resource_type(queryset, request, *args, **kwargs):
- resource_type = request.query_params['resource_type']
+ resource_type = request.query_params["resource_type"]
if queryset.model.__name__.lower() == resource_type.lower():
return queryset
else:
@@ -33,37 +27,38 @@ def filter_resource_type(queryset, request, *args, **kwargs):
def filter_resource_subtype(queryset, request, *args, **kwargs):
- resource_subtype = request.query_params['resource_subtype']
- if queryset.model.__name__ == 'Style':
+ resource_subtype = request.query_params["resource_subtype"]
+ if queryset.model.__name__ == "Style":
return queryset.filter(style_type__name__iexact=resource_subtype)
else:
return queryset.none()
def filter_creator(queryset, request, *args, **kwargs):
- creator = request.query_params['creator']
+ creator = request.query_params["creator"]
qs = queryset.annotate(
- search=(SearchVector('creator__username')
- + SearchVector('creator__first_name')
- + SearchVector('creator__last_name'))
- ).filter(search=creator)
+ search=(
+ SearchVector("creator__username")
+ + SearchVector("creator__first_name")
+ + SearchVector("creator__last_name")
+ )
+ ).filter(search=creator)
return qs
def filter_keyword(queryset, request, *args, **kwargs):
- keyword = request.query_params['keyword']
+ keyword = request.query_params["keyword"]
qs = queryset.annotate(
- search=(
- SearchVector('name') + SearchVector('description'))
- ).filter(search=keyword)
+ search=(SearchVector("name") + SearchVector("description"))
+ ).filter(search=keyword)
return qs
def filter_general(queryset, request, *args, **kwargs):
- resource_type = request.query_params.get('resource_type', None)
- resource_subtype = request.query_params.get('resource_subtype', None)
- creator = request.query_params.get('creator', None)
- keyword = request.query_params.get('keyword', None)
+ resource_type = request.query_params.get("resource_type", None)
+ resource_subtype = request.query_params.get("resource_subtype", None)
+ creator = request.query_params.get("creator", None)
+ keyword = request.query_params.get("keyword", None)
if resource_type:
queryset = filter_resource_type(queryset, request, *args, **kwargs)
if resource_subtype:
@@ -83,40 +78,42 @@ def format_response(self, data):
override the output of pagination
"""
- return OrderedDict([
- ('total', self.total),
- ('next', self.get_next_link()),
- ('previous', self.get_previous_link()),
- ('results', data)
- ])
+ return OrderedDict(
+ [
+ ("total", self.total),
+ ("next", self.get_next_link()),
+ ("previous", self.get_previous_link()),
+ ("results", data),
+ ]
+ )
# cache for 2 hours
-@method_decorator(cache_page(60 * 60 * 2), name='dispatch')
+@method_decorator(cache_page(60 * 60 * 2), name="dispatch")
class ResourceAPIList(FlatMultipleModelAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
pagination_class = LimitPagination
add_model_type = False
- filter_backends = (filters.SearchFilter, )
- search_field = ('name', 'creator')
+ filter_backends = (filters.SearchFilter,)
+ search_field = ("name", "creator")
querylist = [
{
- 'queryset': Geopackage.approved_objects.all(),
- 'serializer_class': GeopackageSerializer,
- 'filter_fn': filter_general
+ "queryset": Geopackage.approved_objects.all(),
+ "serializer_class": GeopackageSerializer,
+ "filter_fn": filter_general,
},
{
- 'queryset': Model.approved_objects.all(),
- 'serializer_class': ModelSerializer,
- 'filter_fn': filter_general
+ "queryset": Model.approved_objects.all(),
+ "serializer_class": ModelSerializer,
+ "filter_fn": filter_general,
},
{
- 'queryset': Style.approved_objects.all(),
- 'serializer_class': StyleSerializer,
- 'filter_fn': filter_general
+ "queryset": Style.approved_objects.all(),
+ "serializer_class": StyleSerializer,
+ "filter_fn": filter_general,
},
]
@@ -131,7 +128,7 @@ class ResourceAPIDownload(APIView):
# Cache page for the requested url
@method_decorator(cache_page(60 * 60 * 2))
def get(self, request, *args, **kwargs):
- uuid = kwargs.get('uuid')
+ uuid = kwargs.get("uuid")
if Geopackage.approved_objects.filter(uuid=uuid).exists():
object = Geopackage.approved_objects.get(uuid=uuid)
elif Model.approved_objects.filter(uuid=uuid).exists():
@@ -147,8 +144,9 @@ def get(self, request, *args, **kwargs):
zipfile = zip_a_file_if_not_zipfile(object.file.file.name)
response = HttpResponse(
- zipfile.getvalue(), content_type="application/x-zip-compressed")
- response['Content-Disposition'] = 'attachment; filename=%s.zip' % (
+ zipfile.getvalue(), content_type="application/x-zip-compressed"
+ )
+ response["Content-Disposition"] = "attachment; filename=%s.zip" % (
slugify(object.name, allow_unicode=True)
)
return response
diff --git a/qgis-app/base/forms/processing_forms.py b/qgis-app/base/forms/processing_forms.py
index 514d13f3..771eb3eb 100644
--- a/qgis-app/base/forms/processing_forms.py
+++ b/qgis-app/base/forms/processing_forms.py
@@ -1,33 +1,44 @@
+from base.validator import filesize_validator
from django import forms
from django.utils.translation import ugettext_lazy as _
-from base.validator import filesize_validator
-
class ResourceBaseReviewForm(forms.Form):
"""Base Review Form for sharing file app."""
- APPROVAL_OPTIONS = [('approve', 'Approve'), ('reject', 'Reject')]
- approval = forms.ChoiceField(required=True, choices=APPROVAL_OPTIONS,
- widget=forms.RadioSelect, initial='approve')
+ APPROVAL_OPTIONS = [("approve", "Approve"), ("reject", "Reject")]
+ approval = forms.ChoiceField(
+ required=True,
+ choices=APPROVAL_OPTIONS,
+ widget=forms.RadioSelect,
+ initial="approve",
+ )
comment = forms.CharField()
def __init__(self, *args, **kwargs):
- self.resource_name = kwargs.pop('resource_name', 'resource')
+ self.resource_name = kwargs.pop("resource_name", "resource")
super(ResourceBaseReviewForm, self).__init__(*args, **kwargs)
- self.fields['comment'].widget = forms.Textarea(
+ self.fields["comment"].widget = forms.Textarea(
attrs={
- 'placeholder': _(
- 'Please provide clear feedback if you decided to not '
- 'approve this %s.') % self.resource_name,
- 'rows': "5"})
-
+ "placeholder": _(
+ "Please provide clear feedback if you decided to not "
+ "approve this %s."
+ )
+ % self.resource_name,
+ "rows": "5",
+ }
+ )
class ResourceBaseSearchForm(forms.Form):
"""Base Search Form for sharing file app."""
- q = forms.CharField(required=False, widget=forms.TextInput(
- attrs={'class': 'search-query', 'placeholder': 'Search'}))
+
+ q = forms.CharField(
+ required=False,
+ widget=forms.TextInput(
+ attrs={"class": "search-query", "placeholder": "Search"}
+ ),
+ )
class ResourceBaseCleanFileForm(object):
@@ -36,6 +47,6 @@ def clean_file(self):
Cleaning file field data.
"""
- file = self.cleaned_data['file']
+ file = self.cleaned_data["file"]
if filesize_validator(file.file):
return file
diff --git a/qgis-app/base/license.py b/qgis-app/base/license.py
index 924045a6..15a58c2a 100644
--- a/qgis-app/base/license.py
+++ b/qgis-app/base/license.py
@@ -24,7 +24,7 @@ def zip_a_file_if_not_zipfile(filename: str) -> io.BytesIO:
""" Zip a file without license """
if zipfile.is_zipfile(filename):
- with open(filename, 'rb') as file:
+ with open(filename, "rb") as file:
return io.BytesIO(file.read())
else:
in_memory_data = io.BytesIO()
diff --git a/qgis-app/base/models/processing_models.py b/qgis-app/base/models/processing_models.py
index 63d8d951..0d6590c5 100644
--- a/qgis-app/base/models/processing_models.py
+++ b/qgis-app/base/models/processing_models.py
@@ -2,35 +2,44 @@
Base Model for sharing file feature
"""
import datetime
-import uuid
import os
+import uuid
-from django.db import models
from django.contrib.auth.models import User
+from django.db import models
from django.utils.translation import ugettext_lazy as _
class UnapprovedManager(models.Manager):
"""Custom Queryset Manager for Unapproved Resource"""
+
def get_queryset(self):
- return super().get_queryset().filter(
- approved=False, require_action=False
- ).order_by('upload_date').distinct()
+ return (
+ super()
+ .get_queryset()
+ .filter(approved=False, require_action=False)
+ .order_by("upload_date")
+ .distinct()
+ )
class ApprovedManager(models.Manager):
"""Custom Queryset Manager for Unapproved Resource"""
+
def get_queryset(self):
- return super().get_queryset().filter(approved=True) \
- .order_by('upload_date')
+ return super().get_queryset().filter(approved=True).order_by("upload_date")
class RequireActionManager(models.Manager):
"""Custom Queryset Manager for reviewed Resource requires an action"""
+
def get_queryset(self):
qs = super().get_queryset()
- return qs.filter(approved=False, require_action=True)\
- .order_by('upload_date').distinct()
+ return (
+ qs.filter(approved=False, require_action=True)
+ .order_by("upload_date")
+ .distinct()
+ )
class Resource(models.Model):
@@ -45,59 +54,68 @@ class Resource(models.Model):
# date
upload_date = models.DateTimeField(
- _('Uploaded on'),
- help_text=_('The upload date. Automatically added on file upload.'),
+ _("Uploaded on"),
+ help_text=_("The upload date. Automatically added on file upload."),
auto_now_add=True,
- editable=False)
+ editable=False,
+ )
modified_date = models.DateTimeField(
- _('Modified on'),
- help_text=_('The upload date. Automatically added on file upload.'),
- editable=False)
+ _("Modified on"),
+ help_text=_("The upload date. Automatically added on file upload."),
+ editable=False,
+ )
# creator
creator = models.ForeignKey(
User,
- verbose_name=_('Created by'),
- help_text=_('The user who uploaded this resource.'),
- on_delete=models.CASCADE)
+ verbose_name=_("Created by"),
+ help_text=_("The user who uploaded this resource."),
+ on_delete=models.CASCADE,
+ )
# name and desc
- name = models.CharField(_('Name'),
- help_text=_('A unique name for this resource'),
- max_length=256,
- blank=False,
- null=False,
- unique=True)
+ name = models.CharField(
+ _("Name"),
+ help_text=_("A unique name for this resource"),
+ max_length=256,
+ blank=False,
+ null=False,
+ unique=True,
+ )
description = models.TextField(
- _('Description'),
- help_text=_('A description of this resource.'),
+ _("Description"),
+ help_text=_("A description of this resource."),
max_length=5000,
blank=False,
- null=False
+ null=False,
)
# counter
download_count = models.IntegerField(
- _('Downloads'),
- help_text=_('The number of times this resource has been downloaded. '
- 'This is updated automatically.'),
+ _("Downloads"),
+ help_text=_(
+ "The number of times this resource has been downloaded. "
+ "This is updated automatically."
+ ),
default=0,
- editable=False)
+ editable=False,
+ )
# approval
approved = models.BooleanField(
- _('Approved'),
+ _("Approved"),
default=False,
- help_text=_('Set to True if you wish to approve this resource.'),
- db_index=True)
+ help_text=_("Set to True if you wish to approve this resource."),
+ db_index=True,
+ )
# require_action
require_action = models.BooleanField(
- _('Requires Action'),
+ _("Requires Action"),
default=False,
- help_text=_('Set to True if you require creator to update the '
- 'resource.'),
- db_index=True)
+ help_text=_("Set to True if you require creator to update the " "resource."),
+ db_index=True,
+ )
# Manager
objects = models.Manager()
@@ -135,33 +153,36 @@ class ResourceReview(models.Model):
"""
A Review Model.
"""
+
# date
review_date = models.DateTimeField(
- _('Reviewed on'),
- help_text=_('The review date. Automatically added on review '
- 'resource.'),
+ _("Reviewed on"),
+ help_text=_("The review date. Automatically added on review " "resource."),
auto_now_add=True,
- editable=False)
+ editable=False,
+ )
# reviewer
reviewer = models.ForeignKey(
User,
- verbose_name=_('Reviewed by'),
- help_text=_('The user who reviewed this %(app_label)s.'),
+ verbose_name=_("Reviewed by"),
+ help_text=_("The user who reviewed this %(app_label)s."),
on_delete=models.CASCADE,
- related_name='%(app_label)s_%(class)s_related')
+ related_name="%(app_label)s_%(class)s_related",
+ )
# comment
comment = models.TextField(
- _('Comment'),
- help_text=_('A review comment. Please write your review.'),
+ _("Comment"),
+ help_text=_("A review comment. Please write your review."),
max_length=1000,
blank=True,
- null=True,)
+ null=True,
+ )
class Meta:
abstract = True
- ordering = ['review_date']
+ ordering = ["review_date"]
def __str__(self):
return self.comment
diff --git a/qgis-app/base/models/site_preferences.py b/qgis-app/base/models/site_preferences.py
index 3e5a6a71..cc8ab6a7 100644
--- a/qgis-app/base/models/site_preferences.py
+++ b/qgis-app/base/models/site_preferences.py
@@ -3,10 +3,10 @@
class SitePreference(Preferences):
- __module__ = 'preferences.models'
+ __module__ = "preferences.models"
qgis_versions = models.TextField(
- default='',
+ default="",
blank=True,
- help_text='QGIS versions that will be used to '
- 'generate the plugins_xml, separated by comma.'
+ help_text="QGIS versions that will be used to "
+ "generate the plugins_xml, separated by comma.",
)
diff --git a/qgis-app/base/tests/test_license.py b/qgis-app/base/tests/test_license.py
index 346f253b..ad8edfe5 100644
--- a/qgis-app/base/tests/test_license.py
+++ b/qgis-app/base/tests/test_license.py
@@ -1,21 +1,17 @@
import os
-
from zipfile import ZipFile
-from django.test import TestCase
-
from base.license import zipped_with_license
+from django.test import TestCase
-TESTFILES_DIR = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'testfiles'))
+TESTFILES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "testfiles"))
class TestZippedWithLicense(TestCase):
-
def setUp(self):
self.file = os.path.join(TESTFILES_DIR, "test.txt")
def test_zipped_with_license(self):
zipfile = zipped_with_license(self.file, "test")
zf = ZipFile(zipfile).namelist()
- self.assertEqual(zf, ['test/test.txt', 'test/license.txt'])
+ self.assertEqual(zf, ["test/test.txt", "test/license.txt"])
diff --git a/qgis-app/base/tests/testfiles/test.txt b/qgis-app/base/tests/testfiles/test.txt
index abd91bd4..ce616e98 100644
--- a/qgis-app/base/tests/testfiles/test.txt
+++ b/qgis-app/base/tests/testfiles/test.txt
@@ -1 +1 @@
-a test file
\ No newline at end of file
+a test file
diff --git a/qgis-app/base/validator.py b/qgis-app/base/validator.py
index 8b88e9aa..534c4c08 100644
--- a/qgis-app/base/validator.py
+++ b/qgis-app/base/validator.py
@@ -4,10 +4,10 @@
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
-RESOURCE_MAX_SIZE = getattr(settings, 'RESOURCE_MAX_SIZE', 1000000) # 1MB
+RESOURCE_MAX_SIZE = getattr(settings, "RESOURCE_MAX_SIZE", 1000000) # 1MB
ERROR_FILESIZE_TOO_BIG = ValidationError(
- _("File is too big. Max size is %s Megabytes") % (
- RESOURCE_MAX_SIZE / 1000000))
+ _("File is too big. Max size is %s Megabytes") % (RESOURCE_MAX_SIZE / 1000000)
+)
def filesize_validator(file) -> bool:
diff --git a/qgis-app/base/views/processing_view.py b/qgis-app/base/views/processing_view.py
index 320ffbea..c0fcbde3 100644
--- a/qgis-app/base/views/processing_view.py
+++ b/qgis-app/base/views/processing_view.py
@@ -1,35 +1,34 @@
import logging
+from base.forms.processing_forms import ResourceBaseReviewForm
+from base.license import zipped_with_license
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin
-from django.contrib.sites.models import Site
from django.contrib.postgres.search import SearchVector
+from django.contrib.sites.models import Site
from django.core.mail import send_mail
from django.db import models
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import reverse, reverse_lazy
-from django.utils.translation import ugettext_lazy as _
from django.utils.decorators import method_decorator
from django.utils.text import slugify
+from django.utils.translation import ugettext_lazy as _
from django.views.decorators.cache import never_cache
-from django.views.generic import (CreateView,
- DetailView,
- DeleteView,
- ListView,
- UpdateView,
- View)
+from django.views.generic import (
+ CreateView,
+ DeleteView,
+ DetailView,
+ ListView,
+ UpdateView,
+ View,
+)
from django.views.generic.base import ContextMixin
-from base.forms.processing_forms import ResourceBaseReviewForm
-
-from base.license import zipped_with_license
-
-
GROUP_NAME = "Style Managers"
@@ -42,16 +41,10 @@ def is_resources_manager(user: User) -> bool:
def check_resources_access(user: User, resource: models.base) -> bool:
"""Check if user is the creator of the resource or is_staff."""
- return (user.is_staff
- or resource.creator == user
- or is_resources_manager(user))
+ return user.is_staff or resource.creator == user or is_resources_manager(user)
-def send_mail_wrapper(subject,
- message,
- mail_from,
- recipients,
- fail_silently=True):
+def send_mail_wrapper(subject, message, mail_from, recipients, fail_silently=True):
"""
Wrapping send_email function to send email only when not DEBUG.
"""
@@ -59,21 +52,19 @@ def send_mail_wrapper(subject,
if settings.DEBUG:
logging.debug("Mail not sent (DEBUG=True)")
else:
- send_mail(subject,
- message,
- mail_from,
- recipients,
- fail_silently)
+ send_mail(subject, message, mail_from, recipients, fail_silently)
-def resource_notify(resource: models.base,
- created=True, resource_type: str = 'Resource') -> None:
+def resource_notify(
+ resource: models.base, created=True, resource_type: str = "Resource"
+) -> None:
"""
Email notification when a new resource created.
"""
- recipients = [u.email for u in User.objects.filter(
- groups__name=GROUP_NAME).exclude(email='')]
+ recipients = [
+ u.email for u in User.objects.filter(groups__name=GROUP_NAME).exclude(email="")
+ ]
if created:
resource_status = "created"
@@ -85,39 +76,50 @@ def resource_notify(resource: models.base,
mail_from = settings.DEFAULT_FROM_EMAIL
send_mail_wrapper(
- _('A new %s has been %s by %s.') % (resource_type, resource_status,
- resource.creator),
- _('\r\n%s name is: %s\r\n%s description is: %s\r\n'
- 'Link: http://%s%s\r\n') % (resource_type, resource.name,
- resource_type, resource.description,
- domain, resource.get_absolute_url()),
+ _("A new %s has been %s by %s.")
+ % (resource_type, resource_status, resource.creator),
+ _("\r\n%s name is: %s\r\n%s description is: %s\r\n" "Link: http://%s%s\r\n")
+ % (
+ resource_type,
+ resource.name,
+ resource_type,
+ resource.description,
+ domain,
+ resource.get_absolute_url(),
+ ),
mail_from,
recipients,
- fail_silently=True)
- logging.debug('Sending email notification for %s %s, '
- 'recipients: %s' % (resource.name, resource_type,
- recipients))
+ fail_silently=True,
+ )
+ logging.debug(
+ "Sending email notification for %s %s, "
+ "recipients: %s" % (resource.name, resource_type, recipients)
+ )
else:
- logging.warning('No recipients found for %s %s notification'
- % (resource.name, resource_type))
+ logging.warning(
+ "No recipients found for %s %s notification"
+ % (resource.name, resource_type)
+ )
-def resource_update_notify(resource: models.base, creator: User, staff: User,
- resource_type: str = 'Resource') -> None:
+def resource_update_notify(
+ resource: models.base, creator: User, staff: User, resource_type: str = "Resource"
+) -> None:
"""
Email notification system when staff approved or rejected a resource
"""
- recipients = [u.email for u in User.objects.filter(
- groups__name=GROUP_NAME).exclude(email='')]
+ recipients = [
+ u.email for u in User.objects.filter(groups__name=GROUP_NAME).exclude(email="")
+ ]
if creator.email:
recipients += [creator.email]
if resource.approved:
- approval_state = 'approved'
+ approval_state = "approved"
else:
- approval_state = 'rejected'
+ approval_state = "rejected"
review = resource.review_set.last()
comment = review.comment
@@ -126,23 +128,30 @@ def resource_update_notify(resource: models.base, creator: User, staff: User,
domain = Site.objects.get_current().domain
mail_from = settings.DEFAULT_FROM_EMAIL
send_mail_wrapper(
- _('%s %s %s notification.') % (
- resource_type, resource, approval_state),
- _('\r\n%s %s %s by %s.\r\n%s\r\nLink: http://%s%s\r\n') % (
- resource_type, resource.name, approval_state, staff, comment,
- domain, resource.get_absolute_url()),
- mail_from,
- recipients,
- fail_silently=True)
- logging.debug('Sending email %s notification for %s Resource, '
- 'recipients: %s' % (approval_state,
- resource,
- recipients))
+ _("%s %s %s notification.") % (resource_type, resource, approval_state),
+ _("\r\n%s %s %s by %s.\r\n%s\r\nLink: http://%s%s\r\n")
+ % (
+ resource_type,
+ resource.name,
+ approval_state,
+ staff,
+ comment,
+ domain,
+ resource.get_absolute_url(),
+ ),
+ mail_from,
+ recipients,
+ fail_silently=True,
+ )
+ logging.debug(
+ "Sending email %s notification for %s Resource, "
+ "recipients: %s" % (approval_state, resource, recipients)
+ )
else:
- logging.warning('No recipients found for %s %s %s '
- 'notification' % (resource,
- resource_type,
- approval_state))
+ logging.warning(
+ "No recipients found for %s %s %s "
+ "notification" % (resource, resource_type, approval_state)
+ )
class ResourceBaseMixin(object):
@@ -150,8 +159,8 @@ class ResourceBaseMixin(object):
Mixin class to provide standard settings for Resource.
"""
- resource_name = 'Resource'
- resource_name_url_base = 'resource'
+ resource_name = "Resource"
+ resource_name_url_base = "resource"
review_model = None
@@ -161,23 +170,25 @@ class ResourceSearchMixin(object):
"""
def get_queryset_search(self, qs):
- q = self.request.GET.get('q')
+ q = self.request.GET.get("q")
if q:
qs = qs.annotate(
- search=(SearchVector('name')
- + SearchVector('description')
- + SearchVector('creator__username')
- + SearchVector('creator__first_name')
- + SearchVector('creator__last_name'))
+ search=(
+ SearchVector("name")
+ + SearchVector("description")
+ + SearchVector("creator__username")
+ + SearchVector("creator__first_name")
+ + SearchVector("creator__last_name")
+ )
).filter(search=q)
- order_by = self.request.GET.get('order_by', None)
+ order_by = self.request.GET.get("order_by", None)
if order_by:
# for style sharing app, there is style_type column that doesn't
# exist in deafult sharing app
if order_by == "-type":
- qs = qs.order_by('-style_type__name')
+ qs = qs.order_by("-style_type__name")
elif order_by == "type":
- qs = qs.order_by('style_type__name')
+ qs = qs.order_by("style_type__name")
else:
qs = qs.order_by(order_by)
return qs
@@ -195,37 +206,35 @@ def get_context_data(self, **kwargs):
context = super(ResourceBaseContextMixin, self).get_context_data()
# set the app page name
- context['resource_name'] = self.resource_name
+ context["resource_name"] = self.resource_name
# url name rendered on template
- context['url_create'] = "%s_create" % self.resource_name_url_base
- context['url_list'] = "%s_list" % self.resource_name_url_base
- context['url_unapproved'] = "%s_unapproved" % (
- self.resource_name_url_base)
- context['url_require_action'] = "%s_require_action" % (
- self.resource_name_url_base)
- context['url_nav_content'] = "%s_nav_content" % (
- self.resource_name_url_base)
-
- context['url_download'] = "%s_download" % self.resource_name_url_base
- context['url_update'] = "%s_update" % self.resource_name_url_base
- context['url_delete'] = "%s_delete" % self.resource_name_url_base
- context['url_review'] = "%s_review" % self.resource_name_url_base
- context['url_detail'] = "%s_detail" % self.resource_name_url_base
+ context["url_create"] = "%s_create" % self.resource_name_url_base
+ context["url_list"] = "%s_list" % self.resource_name_url_base
+ context["url_unapproved"] = "%s_unapproved" % (self.resource_name_url_base)
+ context["url_require_action"] = "%s_require_action" % (
+ self.resource_name_url_base
+ )
+ context["url_nav_content"] = "%s_nav_content" % (self.resource_name_url_base)
+
+ context["url_download"] = "%s_download" % self.resource_name_url_base
+ context["url_update"] = "%s_update" % self.resource_name_url_base
+ context["url_delete"] = "%s_delete" % self.resource_name_url_base
+ context["url_review"] = "%s_review" % self.resource_name_url_base
+ context["url_detail"] = "%s_detail" % self.resource_name_url_base
return context
-@method_decorator(never_cache, name='dispatch')
-class ResourceBaseCreateView(LoginRequiredMixin,
- ResourceBaseContextMixin,
- SuccessMessageMixin,
- CreateView):
+@method_decorator(never_cache, name="dispatch")
+class ResourceBaseCreateView(
+ LoginRequiredMixin, ResourceBaseContextMixin, SuccessMessageMixin, CreateView
+):
"""Upload a Resource File.
We don't cache since there's a dynamic preference value on the template
"""
- template_name = 'base/upload_form.html'
+ template_name = "base/upload_form.html"
is_1mb_limit_enable = True
is_custom_license_agreement = False
@@ -235,29 +244,27 @@ def form_valid(self, form):
self.obj.save()
resource_notify(self.obj, resource_type=self.resource_name)
msg = _(self.success_message)
- messages.success(self.request, msg, 'success', fail_silently=True)
+ messages.success(self.request, msg, "success", fail_silently=True)
return super(ResourceBaseCreateView, self).form_valid(form)
def get_success_message(self, cleaned_data):
return "%s was uploaded successfully." % self.resource_name
def get_success_url(self):
- url_name = '%s_detail' % self.resource_name_url_base
- return reverse(url_name, kwargs={'pk': self.object.id})
+ url_name = "%s_detail" % self.resource_name_url_base
+ return reverse(url_name, kwargs={"pk": self.object.id})
def get_context_data(self, **kwargs):
context = super().get_context_data()
- context['limit_1mb'] = self.is_1mb_limit_enable
- context['is_custom_license_agreement'] = \
- self.is_custom_license_agreement
+ context["limit_1mb"] = self.is_1mb_limit_enable
+ context["is_custom_license_agreement"] = self.is_custom_license_agreement
return context
-class ResourceBaseDetailView(ResourceBaseContextMixin,
- DetailView):
+class ResourceBaseDetailView(ResourceBaseContextMixin, DetailView):
"""Base Class for Resource DetailView."""
- context_object_name = 'object_detail'
+ context_object_name = "object_detail"
# js source files
# e.g. js = ({'src': 'path/to/js/under/static/file.js', 'type': 'module'},)
@@ -269,13 +276,13 @@ class ResourceBaseDetailView(ResourceBaseContextMixin,
css = ()
is_3d_model = False
- license_template = 'base/includes/license.html'
+ license_template = "base/includes/license.html"
def get_template_names(self):
object = self.get_object()
if not object.approved:
- return 'base/review.html'
- return 'base/detail.html'
+ return "base/review.html"
+ return "base/detail.html"
def get_queryset(self):
return self.model.objects.all()
@@ -283,37 +290,32 @@ def get_queryset(self):
def get_context_data(self, **kwargs):
context = super().get_context_data()
user = self.request.user
- context['creator'] = self.object.get_creator_name
- context['js'] = self.js
- context['css'] = self.css
- context['is_3d_model'] = self.is_3d_model
- context['license_template'] = self.license_template
+ context["creator"] = self.object.get_creator_name
+ context["js"] = self.js
+ context["css"] = self.css
+ context["is_3d_model"] = self.is_3d_model
+ context["license_template"] = self.license_template
if self.object.review_set.exists():
if self.object.review_set.last().reviewer.first_name:
reviewer = "%s %s" % (
- self.object.review_set.last()
- .reviewer.first_name,
- self.object.review_set.last().reviewer.last_name)
+ self.object.review_set.last().reviewer.first_name,
+ self.object.review_set.last().reviewer.last_name,
+ )
else:
- reviewer = self.object.review_set.last().reviewer \
- .username
- context['reviewer'] = reviewer
+ reviewer = self.object.review_set.last().reviewer.username
+ context["reviewer"] = reviewer
if user.is_staff or is_resources_manager(user):
- context['form'] = ResourceBaseReviewForm(
- resource_name=self.resource_name
- )
+ context["form"] = ResourceBaseReviewForm(resource_name=self.resource_name)
if self.is_3d_model:
- context['url_viewer'] = "%s_viewer" % self.resource_name_url_base
+ context["url_viewer"] = "%s_viewer" % self.resource_name_url_base
return context
-class ResourceBaseUpdateView(LoginRequiredMixin,
- ResourceBaseContextMixin,
- UpdateView):
+class ResourceBaseUpdateView(LoginRequiredMixin, ResourceBaseContextMixin, UpdateView):
"""Update Resource"""
- context_object_name = 'object'
- template_name = 'base/update_form.html'
+ context_object_name = "object"
+ template_name = "base/update_form.html"
is_1mb_limit_enable = True
is_custom_license_agreement = False
@@ -321,7 +323,7 @@ def dispatch(self, request, *args, **kwargs):
object = self.get_object()
user = self.request.user
if not check_resources_access(user, object):
- url_name = '%s_list' % self.resource_name_url_base
+ url_name = "%s_list" % self.resource_name_url_base
return HttpResponseRedirect(reverse(url_name))
return super().dispatch(request, *args, **kwargs)
@@ -332,32 +334,28 @@ def form_valid(self, form):
obj.save()
resource_notify(obj, created=False, resource_type=self.resource_name)
msg = _("The %s has been successfully updated." % self.resource_name)
- messages.success(self.request, msg, 'success', fail_silently=True)
- url_name = '%s_detail' % self.resource_name_url_base
- return HttpResponseRedirect(reverse_lazy(url_name,
- kwargs={'pk': obj.id}))
+ messages.success(self.request, msg, "success", fail_silently=True)
+ url_name = "%s_detail" % self.resource_name_url_base
+ return HttpResponseRedirect(reverse_lazy(url_name, kwargs={"pk": obj.id}))
def get_context_data(self, **kwargs):
context = super().get_context_data()
- context['limit_1mb'] = self.is_1mb_limit_enable
- context['is_custom_license_agreement'] = \
- self.is_custom_license_agreement
+ context["limit_1mb"] = self.is_1mb_limit_enable
+ context["is_custom_license_agreement"] = self.is_custom_license_agreement
return context
-@method_decorator(never_cache, name='dispatch')
-class ResourceBaseListView(ResourceBaseContextMixin,
- ResourceSearchMixin,
- ListView):
+@method_decorator(never_cache, name="dispatch")
+class ResourceBaseListView(ResourceBaseContextMixin, ResourceSearchMixin, ListView):
- context_object_name = 'object_list'
+ context_object_name = "object_list"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['count'] = self.get_queryset().count()
- context['order_by'] = self.request.GET.get('order_by', None)
- context['queries'] = self.request.GET.get('q', None)
- context['is_gallery'] = self.request.GET.get('is_gallery', None)
+ context["count"] = self.get_queryset().count()
+ context["order_by"] = self.request.GET.get("order_by", None)
+ context["queries"] = self.request.GET.get("q", None)
+ context["is_gallery"] = self.request.GET.get("is_gallery", None)
return context
def get_queryset(self):
@@ -367,24 +365,23 @@ def get_queryset(self):
def get_template_names(self):
context = self.get_context_data()
- is_gallery = context['is_gallery']
+ is_gallery = context["is_gallery"]
if is_gallery:
self.paginate_by = settings.PAGINATION_DEFAULT_PAGINATION
- return 'base/list_galery.html'
+ return "base/list_galery.html"
else:
- return 'base/list.html'
+ return "base/list.html"
def get_paginate_by(self, queryset):
- is_gallery = self.request.GET.get('is_gallery', None)
+ is_gallery = self.request.GET.get("is_gallery", None)
if is_gallery:
return settings.PAGINATION_DEFAULT_PAGINATION_HUB
return settings.PAGINATION_DEFAULT_PAGINATION
-class ResourceBaseUnapprovedListView(LoginRequiredMixin,
- ResourceBaseListView,
- ResourceSearchMixin):
-
+class ResourceBaseUnapprovedListView(
+ LoginRequiredMixin, ResourceBaseListView, ResourceSearchMixin
+):
def get_queryset(self):
qs = self.model.unapproved_objects.all()
qs = self.get_queryset_search_and_is_creator(qs)
@@ -392,14 +389,13 @@ def get_queryset(self):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['title'] = 'Waiting Review'
+ context["title"] = "Waiting Review"
return context
-class ResourceBaseRequireActionListView(LoginRequiredMixin,
- ResourceBaseListView,
- ResourceSearchMixin):
-
+class ResourceBaseRequireActionListView(
+ LoginRequiredMixin, ResourceBaseListView, ResourceSearchMixin
+):
def get_queryset(self):
qs = self.model.requireaction_objects.all()
qs = self.get_queryset_search_and_is_creator(qs)
@@ -407,78 +403,76 @@ def get_queryset(self):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['title'] = 'Requiring Update'
+ context["title"] = "Requiring Update"
return context
-class ResourceBaseDeleteView(LoginRequiredMixin,
- ResourceBaseContextMixin,
- DeleteView):
+class ResourceBaseDeleteView(LoginRequiredMixin, ResourceBaseContextMixin, DeleteView):
"""
Delete a resource instance.
"""
- context_object_file = 'object'
- template_name = 'base/confirm_delete.html'
+ context_object_file = "object"
+ template_name = "base/confirm_delete.html"
def dispatch(self, request, *args, **kwargs):
object = self.get_object()
user = self.request.user
if not check_resources_access(user, object):
- url_name = '%s_list' % self.resource_name_url_base
+ url_name = "%s_list" % self.resource_name_url_base
return HttpResponseRedirect(reverse(url_name))
return super().dispatch(request, *args, **kwargs)
def get_success_url(self):
- url_name = '%s_list' % self.resource_name_url_base
+ url_name = "%s_list" % self.resource_name_url_base
return reverse_lazy(url_name)
class ResourceBaseReviewView(ResourceBaseMixin, View):
form_class = ResourceBaseReviewForm
- http_method_names = ['post']
+ http_method_names = ["post"]
def post(self, request, *args, **kwargs):
- object = get_object_or_404(self.model, pk=self.kwargs['pk'])
+ object = get_object_or_404(self.model, pk=self.kwargs["pk"])
form = self.form_class(request.POST)
if form.is_valid():
data = form.cleaned_data
self.review_model.objects.create(
- resource=object,
- reviewer=request.user,
- comment=data['comment'])
- if data['approval'] == 'approve':
+ resource=object, reviewer=request.user, comment=data["comment"]
+ )
+ if data["approval"] == "approve":
object.approved = True
object.require_action = False
msg = _("The %s has been approved." % self.resource_name)
- messages.success(request, msg, 'success', fail_silently=True)
+ messages.success(request, msg, "success", fail_silently=True)
else:
object.approved = False
object.require_action = True
msg = _("The %s has been rejected." % self.resource_name)
- messages.success(request, msg, 'error', fail_silently=True)
+ messages.success(request, msg, "error", fail_silently=True)
object.save()
# send email notification
- resource_update_notify(object, object.creator, request.user,
- self.resource_name)
- url_name = '%s_detail' % self.resource_name_url_base
- return HttpResponseRedirect(
- reverse(url_name, kwargs={'pk': self.kwargs['pk']}))
+ resource_update_notify(
+ object, object.creator, request.user, self.resource_name
+ )
+ url_name = "%s_detail" % self.resource_name_url_base
+ return HttpResponseRedirect(reverse(url_name, kwargs={"pk": self.kwargs["pk"]}))
class ResourceBaseDownload(ResourceBaseContextMixin, View):
"""Download resource files and zip it with license."""
- template_name = 'base/permission_deny.html'
+ template_name = "base/permission_deny.html"
def get(self, request, *args, **kwargs):
- object = get_object_or_404(self.model, pk=self.kwargs['pk'])
+ object = get_object_or_404(self.model, pk=self.kwargs["pk"])
if not object.approved:
if not check_resources_access(self.request.user, object):
context = super(ResourceBaseDownload, self).get_context_data()
- context['object_name'] = object.name
- context['context'] = ('Download failed. This %s is '
- 'not approved' % self.resource_name)
+ context["object_name"] = object.name
+ context["context"] = (
+ "Download failed. This %s is " "not approved" % self.resource_name
+ )
return TemplateResponse(request, self.template_name, context)
else:
object.increase_download_counter()
@@ -488,8 +482,9 @@ def get(self, request, *args, **kwargs):
zipfile = zipped_with_license(object.file.file.name, object.name)
response = HttpResponse(
- zipfile.getvalue(), content_type="application/x-zip-compressed")
- response['Content-Disposition'] = 'attachment; filename=%s.zip' % (
+ zipfile.getvalue(), content_type="application/x-zip-compressed"
+ )
+ response["Content-Disposition"] = "attachment; filename=%s.zip" % (
slugify(object.name, allow_unicode=True)
)
return response
@@ -509,11 +504,15 @@ def resource_nav_content(request, model):
waiting_review = model.unapproved_objects.distinct().count()
require_action = model.requireaction_objects.distinct().count()
elif user.is_authenticated:
- waiting_review = model.unapproved_objects.filter(
- creator=user).distinct().count()
- require_action = model.requireaction_objects.filter(
- creator=user).distinct().count()
- count_object = {'all': all_object,
- 'waiting_review': waiting_review,
- 'require_action': require_action}
+ waiting_review = (
+ model.unapproved_objects.filter(creator=user).distinct().count()
+ )
+ require_action = (
+ model.requireaction_objects.filter(creator=user).distinct().count()
+ )
+ count_object = {
+ "all": all_object,
+ "waiting_review": waiting_review,
+ "require_action": require_action,
+ }
return JsonResponse(count_object, status=200)
diff --git a/qgis-app/custom_haystack_urls.py b/qgis-app/custom_haystack_urls.py
index 3d5da176..a0ab8c23 100755
--- a/qgis-app/custom_haystack_urls.py
+++ b/qgis-app/custom_haystack_urls.py
@@ -1,24 +1,28 @@
# Custom haystack search to match partial strings
-from haystack.views import SearchView
-from haystack.query import SearchQuerySet
from django.conf.urls import include, url
+from haystack.query import SearchQuerySet
+from haystack.views import SearchView
class SearchWithRequest(SearchView):
- __qualname__ = 'SearchWithRequest'
+ __qualname__ = "SearchWithRequest"
def build_form(self, form_kwargs=None):
if form_kwargs is None:
form_kwargs = {}
if self.searchqueryset is None:
- sqs1 = SearchQuerySet().filter(description_auto=self.request.GET.get('q', ''))
- sqs2 = SearchQuerySet().filter(name_auto=self.request.GET.get('q', ''))
- sqs3 = SearchQuerySet().filter(text=self.request.GET.get('q', ''))
- sqs4 = SearchQuerySet().filter(package_name_auto=self.request.GET.get('q', ''))
- form_kwargs['searchqueryset'] = sqs1 | sqs2 | sqs3 | sqs4
+ sqs1 = SearchQuerySet().filter(
+ description_auto=self.request.GET.get("q", "")
+ )
+ sqs2 = SearchQuerySet().filter(name_auto=self.request.GET.get("q", ""))
+ sqs3 = SearchQuerySet().filter(text=self.request.GET.get("q", ""))
+ sqs4 = SearchQuerySet().filter(
+ package_name_auto=self.request.GET.get("q", "")
+ )
+ form_kwargs["searchqueryset"] = sqs1 | sqs2 | sqs3 | sqs4
return super(SearchWithRequest, self).build_form(form_kwargs)
@@ -30,5 +34,5 @@ def get_results(self):
urlpatterns = [
- url(r'^$', SearchWithRequest(load_all=False), {}, name='haystack_search'),
+ url(r"^$", SearchWithRequest(load_all=False), {}, name="haystack_search"),
]
diff --git a/qgis-app/fixtures/auth.json b/qgis-app/fixtures/auth.json
index 2e5081f4..16588237 100644
--- a/qgis-app/fixtures/auth.json
+++ b/qgis-app/fixtures/auth.json
@@ -1,56 +1,56 @@
[
{
- "pk": 2,
- "model": "auth.user",
+ "pk": 2,
+ "model": "auth.user",
"fields": {
- "username": "creator",
- "first_name": "",
- "last_name": "",
- "is_active": true,
- "is_superuser": false,
- "is_staff": false,
- "last_login": "2010-11-24 07:56:12",
- "groups": [],
- "user_permissions": [],
- "password": "sha1$d6c11$4f3f04e104dc8bbe7950234f0cd8406a65df0bdf",
- "email": "",
+ "username": "creator",
+ "first_name": "",
+ "last_name": "",
+ "is_active": true,
+ "is_superuser": false,
+ "is_staff": false,
+ "last_login": "2010-11-24 07:56:12",
+ "groups": [],
+ "user_permissions": [],
+ "password": "sha1$d6c11$4f3f04e104dc8bbe7950234f0cd8406a65df0bdf",
+ "email": "",
"date_joined": "2010-11-24 07:56:12"
}
- },
+ },
{
- "pk": 1,
- "model": "auth.user",
+ "pk": 1,
+ "model": "auth.user",
"fields": {
- "username": "admin",
- "first_name": "",
- "last_name": "",
- "is_active": true,
- "is_superuser": true,
- "is_staff": true,
- "last_login": "2010-11-25 07:35:07",
- "groups": [],
- "user_permissions": [],
- "password": "sha1$9ba9f$6088ef8abc2243a55e777e937159c8f2fd4920bb",
- "email": "admin@admin.it",
+ "username": "admin",
+ "first_name": "",
+ "last_name": "",
+ "is_active": true,
+ "is_superuser": true,
+ "is_staff": true,
+ "last_login": "2010-11-25 07:35:07",
+ "groups": [],
+ "user_permissions": [],
+ "password": "sha1$9ba9f$6088ef8abc2243a55e777e937159c8f2fd4920bb",
+ "email": "admin@admin.it",
"date_joined": "2009-10-06 18:04:20"
}
- },
+ },
{
- "pk": 3,
- "model": "auth.user",
+ "pk": 3,
+ "model": "auth.user",
"fields": {
- "username": "staff",
- "first_name": "",
- "last_name": "",
- "is_active": true,
- "is_superuser": false,
- "is_staff": true,
- "last_login": "2010-11-25 07:35:20",
- "groups": [],
- "user_permissions": [],
- "password": "sha1$cb97a$221727796b3f551e342dca9d00112f072e399182",
- "email": "",
+ "username": "staff",
+ "first_name": "",
+ "last_name": "",
+ "is_active": true,
+ "is_superuser": false,
+ "is_staff": true,
+ "last_login": "2010-11-25 07:35:20",
+ "groups": [],
+ "user_permissions": [],
+ "password": "sha1$cb97a$221727796b3f551e342dca9d00112f072e399182",
+ "email": "",
"date_joined": "2010-11-25 07:35:20"
}
}
-]
\ No newline at end of file
+]
diff --git a/qgis-app/fixtures/plugins.json b/qgis-app/fixtures/plugins.json
index 2bc21c58..3bc7955b 100644
--- a/qgis-app/fixtures/plugins.json
+++ b/qgis-app/fixtures/plugins.json
@@ -87,4 +87,4 @@
"experimental": true
}
}
-]
\ No newline at end of file
+]
diff --git a/qgis-app/fixtures/simplemenu.json b/qgis-app/fixtures/simplemenu.json
index b04dbdc5..7f8ca47d 100644
--- a/qgis-app/fixtures/simplemenu.json
+++ b/qgis-app/fixtures/simplemenu.json
@@ -1,3 +1,3 @@
[{"model": "simplemenu.menu", "pk": 1, "fields": {"name": "Navigation"}},
{"model": "simplemenu.menu", "pk": 2, "fields": {"name": "Hub"}}
-]
\ No newline at end of file
+]
diff --git a/qgis-app/fixtures/styles.json b/qgis-app/fixtures/styles.json
index 39dc8bd8..a6776dfa 100644
--- a/qgis-app/fixtures/styles.json
+++ b/qgis-app/fixtures/styles.json
@@ -91,4 +91,4 @@
"comment": "This style is rejected for testing purpose."
}
}
-]
\ No newline at end of file
+]
diff --git a/qgis-app/geopackages/admin.py b/qgis-app/geopackages/admin.py
index 7b4ef29d..2b0e3123 100644
--- a/qgis-app/geopackages/admin.py
+++ b/qgis-app/geopackages/admin.py
@@ -4,16 +4,31 @@
class GeopackageInline(admin.TabularInline):
model = Review
- list_display = ('review_date', 'comment', 'reviewer')
+ list_display = ("review_date", "comment", "reviewer")
@admin.register(Geopackage)
class GeopackageAdmin(admin.ModelAdmin):
- inlines = [GeopackageInline, ]
- list_display = ('name', 'description', 'creator', 'upload_date',)
- search_fields = ('name', 'description',)
+ inlines = [
+ GeopackageInline,
+ ]
+ list_display = (
+ "name",
+ "description",
+ "creator",
+ "upload_date",
+ )
+ search_fields = (
+ "name",
+ "description",
+ )
@admin.register(Review)
class GeopackageReviewAdmin(admin.ModelAdmin):
- list_display = ('resource', 'reviewer', 'comment', 'review_date',)
+ list_display = (
+ "resource",
+ "reviewer",
+ "comment",
+ "review_date",
+ )
diff --git a/qgis-app/geopackages/apps.py b/qgis-app/geopackages/apps.py
index 63f2d692..a95defe7 100644
--- a/qgis-app/geopackages/apps.py
+++ b/qgis-app/geopackages/apps.py
@@ -2,4 +2,4 @@
class GeopackagesConfig(AppConfig):
- name = 'geopackages'
+ name = "geopackages"
diff --git a/qgis-app/geopackages/forms.py b/qgis-app/geopackages/forms.py
index 474682c0..a70bb76d 100644
--- a/qgis-app/geopackages/forms.py
+++ b/qgis-app/geopackages/forms.py
@@ -1,14 +1,17 @@
+from base.forms.processing_forms import ResourceBaseCleanFileForm
from django import forms
-
from geopackages.models import Geopackage
-from base.forms.processing_forms import ResourceBaseCleanFileForm
-
class ResourceFormMixin(forms.ModelForm):
class Meta:
model = Geopackage
- fields = ['file', 'thumbnail_image', 'name', 'description', ]
+ fields = [
+ "file",
+ "thumbnail_image",
+ "name",
+ "description",
+ ]
class UploadForm(ResourceBaseCleanFileForm, ResourceFormMixin):
diff --git a/qgis-app/geopackages/migrations/0001_initial.py b/qgis-app/geopackages/migrations/0001_initial.py
index f58a81f6..0f3aba02 100644
--- a/qgis-app/geopackages/migrations/0001_initial.py
+++ b/qgis-app/geopackages/migrations/0001_initial.py
@@ -1,9 +1,9 @@
# Generated by Django 2.2 on 2020-12-01 20:23
-from django.conf import settings
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -16,32 +16,162 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
- name='Geopackage',
+ name="Geopackage",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('upload_date', models.DateTimeField(auto_now_add=True, help_text='The upload date. Automatically added on file upload.', verbose_name='Uploaded on')),
- ('modified_date', models.DateTimeField(editable=False, help_text='The upload date. Automatically added on file upload.', verbose_name='Modified on')),
- ('name', models.CharField(help_text='A non-unique name for this GeoPackage.', max_length=256, unique=True, verbose_name='Name')),
- ('description', models.TextField(help_text='A description of this GeoPackage.', max_length=5000, verbose_name='Description')),
- ('thumbnail_image', models.ImageField(help_text='Please upload an image that demonstrate this GeoPackage.', upload_to='geopackages/%Y', verbose_name='Thumbnail')),
- ('gpkg_file', models.FileField(help_text='A GeoPackage file. The filesize must less than 1MB ', upload_to='geopackages/%Y', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['gpkg'])], verbose_name='GeoPackage file')),
- ('download_count', models.IntegerField(default=0, editable=False, help_text='The number of times this GeoPackage has been downloaded. This is updated automatically.', verbose_name='Downloads')),
- ('approved', models.BooleanField(db_index=True, default=False, help_text='Set to True if you wish to approve this GeoPackage.', verbose_name='Approved')),
- ('require_action', models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update its GeoPackage.', verbose_name='Requires Action')),
- ('creator', models.ForeignKey(help_text='The user who uploaded this style.', on_delete=django.db.models.deletion.CASCADE, related_name='gpkg_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "upload_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Uploaded on",
+ ),
+ ),
+ (
+ "modified_date",
+ models.DateTimeField(
+ editable=False,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Modified on",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ help_text="A non-unique name for this GeoPackage.",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
+ ),
+ (
+ "description",
+ models.TextField(
+ help_text="A description of this GeoPackage.",
+ max_length=5000,
+ verbose_name="Description",
+ ),
+ ),
+ (
+ "thumbnail_image",
+ models.ImageField(
+ help_text="Please upload an image that demonstrate this GeoPackage.",
+ upload_to="geopackages/%Y",
+ verbose_name="Thumbnail",
+ ),
+ ),
+ (
+ "gpkg_file",
+ models.FileField(
+ help_text="A GeoPackage file. The filesize must less than 1MB ",
+ upload_to="geopackages/%Y",
+ validators=[
+ django.core.validators.FileExtensionValidator(
+ allowed_extensions=["gpkg"]
+ )
+ ],
+ verbose_name="GeoPackage file",
+ ),
+ ),
+ (
+ "download_count",
+ models.IntegerField(
+ default=0,
+ editable=False,
+ help_text="The number of times this GeoPackage has been downloaded. This is updated automatically.",
+ verbose_name="Downloads",
+ ),
+ ),
+ (
+ "approved",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you wish to approve this GeoPackage.",
+ verbose_name="Approved",
+ ),
+ ),
+ (
+ "require_action",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update its GeoPackage.",
+ verbose_name="Requires Action",
+ ),
+ ),
+ (
+ "creator",
+ models.ForeignKey(
+ help_text="The user who uploaded this style.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="gpkg_created_by",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
+ ),
],
),
migrations.CreateModel(
- name='GeopackageReview',
+ name="GeopackageReview",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('review_date', models.DateTimeField(auto_now_add=True, help_text='The review date. Automatically added on GeoPackage review.', verbose_name='Reviewed on')),
- ('comment', models.TextField(blank=True, help_text='A review comment. Please write your review.', max_length=1000, null=True, verbose_name='Comment')),
- ('geopackage', models.ForeignKey(help_text='The reviewed GeoPackage', on_delete=django.db.models.deletion.CASCADE, to='geopackages.Geopackage', verbose_name='GeoPackage')),
- ('reviewer', models.ForeignKey(help_text='The user who reviewed this GeoPackage.', on_delete=django.db.models.deletion.CASCADE, related_name='geopackage_reviewed_by', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "review_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The review date. Automatically added on GeoPackage review.",
+ verbose_name="Reviewed on",
+ ),
+ ),
+ (
+ "comment",
+ models.TextField(
+ blank=True,
+ help_text="A review comment. Please write your review.",
+ max_length=1000,
+ null=True,
+ verbose_name="Comment",
+ ),
+ ),
+ (
+ "geopackage",
+ models.ForeignKey(
+ help_text="The reviewed GeoPackage",
+ on_delete=django.db.models.deletion.CASCADE,
+ to="geopackages.Geopackage",
+ verbose_name="GeoPackage",
+ ),
+ ),
+ (
+ "reviewer",
+ models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="geopackage_reviewed_by",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
+ ),
],
options={
- 'ordering': ['review_date'],
+ "ordering": ["review_date"],
},
),
]
diff --git a/qgis-app/geopackages/migrations/0002_auto_20201215_2123.py b/qgis-app/geopackages/migrations/0002_auto_20201215_2123.py
index 3c436ae6..4117af17 100644
--- a/qgis-app/geopackages/migrations/0002_auto_20201215_2123.py
+++ b/qgis-app/geopackages/migrations/0002_auto_20201215_2123.py
@@ -1,66 +1,117 @@
# Generated by Django 2.2.13 on 2020-12-15 21:23
-from django.conf import settings
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('geopackages', '0001_initial'),
+ ("geopackages", "0001_initial"),
]
operations = [
migrations.AlterField(
- model_name='geopackage',
- name='approved',
- field=models.BooleanField(db_index=True, default=False, help_text='Set to True if you wish to approve this resource.', verbose_name='Approved'),
+ model_name="geopackage",
+ name="approved",
+ field=models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you wish to approve this resource.",
+ verbose_name="Approved",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='creator',
- field=models.ForeignKey(help_text='The user who uploaded this resource.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created by'),
+ model_name="geopackage",
+ name="creator",
+ field=models.ForeignKey(
+ help_text="The user who uploaded this resource.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='description',
- field=models.TextField(help_text='A description of this resource.', max_length=5000, verbose_name='Description'),
+ model_name="geopackage",
+ name="description",
+ field=models.TextField(
+ help_text="A description of this resource.",
+ max_length=5000,
+ verbose_name="Description",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='download_count',
- field=models.IntegerField(default=0, editable=False, help_text='The number of times this resource has been downloaded. This is updated automatically.', verbose_name='Downloads'),
+ model_name="geopackage",
+ name="download_count",
+ field=models.IntegerField(
+ default=0,
+ editable=False,
+ help_text="The number of times this resource has been downloaded. This is updated automatically.",
+ verbose_name="Downloads",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='gpkg_file',
- field=models.FileField(help_text='A GeoPackage file. The filesize must less than 1MB ', upload_to='geopackages/%Y', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['gpkg', 'zip'])], verbose_name='GeoPackage file'),
+ model_name="geopackage",
+ name="gpkg_file",
+ field=models.FileField(
+ help_text="A GeoPackage file. The filesize must less than 1MB ",
+ upload_to="geopackages/%Y",
+ validators=[
+ django.core.validators.FileExtensionValidator(
+ allowed_extensions=["gpkg", "zip"]
+ )
+ ],
+ verbose_name="GeoPackage file",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='name',
- field=models.CharField(help_text='A unique name for this resource', max_length=256, unique=True, verbose_name='Name'),
+ model_name="geopackage",
+ name="name",
+ field=models.CharField(
+ help_text="A unique name for this resource",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='require_action',
- field=models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update the resource.', verbose_name='Requires Action'),
+ model_name="geopackage",
+ name="require_action",
+ field=models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update the resource.",
+ verbose_name="Requires Action",
+ ),
),
migrations.AlterField(
- model_name='geopackage',
- name='thumbnail_image',
- field=models.ImageField(help_text='Please upload an image that demonstrate this resource.', upload_to='geopackages/%Y', verbose_name='Thumbnail'),
+ model_name="geopackage",
+ name="thumbnail_image",
+ field=models.ImageField(
+ help_text="Please upload an image that demonstrate this resource.",
+ upload_to="geopackages/%Y",
+ verbose_name="Thumbnail",
+ ),
),
migrations.AlterField(
- model_name='geopackagereview',
- name='review_date',
- field=models.DateTimeField(auto_now_add=True, help_text='The review date. Automatically added on review resource.', verbose_name='Reviewed on'),
+ model_name="geopackagereview",
+ name="review_date",
+ field=models.DateTimeField(
+ auto_now_add=True,
+ help_text="The review date. Automatically added on review resource.",
+ verbose_name="Reviewed on",
+ ),
),
migrations.AlterField(
- model_name='geopackagereview',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this GeoPackage.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="geopackagereview",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/geopackages/migrations/0003_rename_Review_model_and_file_field.py b/qgis-app/geopackages/migrations/0003_rename_Review_model_and_file_field.py
index d9b4fd6a..ec3f9e8b 100644
--- a/qgis-app/geopackages/migrations/0003_rename_Review_model_and_file_field.py
+++ b/qgis-app/geopackages/migrations/0003_rename_Review_model_and_file_field.py
@@ -8,25 +8,19 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('geopackages', '0002_auto_20201215_2123'),
+ ("geopackages", "0002_auto_20201215_2123"),
]
operations = [
migrations.RenameField(
- model_name='geopackage',
- old_name='gpkg_file',
- new_name='file',
+ model_name="geopackage",
+ old_name="gpkg_file",
+ new_name="file",
),
-
migrations.RenameField(
- model_name='geopackagereview',
- old_name='geopackage',
- new_name='resource',
+ model_name="geopackagereview",
+ old_name="geopackage",
+ new_name="resource",
),
-
- migrations.RenameModel(
- old_name='GeopackageReview',
- new_name='Review'
- )
-
+ migrations.RenameModel(old_name="GeopackageReview", new_name="Review"),
]
diff --git a/qgis-app/geopackages/migrations/0004_auto_20210121_0227.py b/qgis-app/geopackages/migrations/0004_auto_20210121_0227.py
index 3fd6425a..fcc1d701 100644
--- a/qgis-app/geopackages/migrations/0004_auto_20210121_0227.py
+++ b/qgis-app/geopackages/migrations/0004_auto_20210121_0227.py
@@ -1,20 +1,26 @@
# Generated by Django 2.2.13 on 2021-01-21 02:27
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
- ('geopackages', '0003_rename_Review_model_and_file_field'),
+ ("geopackages", "0003_rename_Review_model_and_file_field"),
]
operations = [
migrations.AlterField(
- model_name='review',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this GeoPackage.', on_delete=django.db.models.deletion.CASCADE, related_name='geopackages_review_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="review",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="geopackages_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/geopackages/migrations/0005_auto_20210203_2142.py b/qgis-app/geopackages/migrations/0005_auto_20210203_2142.py
index aec9258e..510d5393 100644
--- a/qgis-app/geopackages/migrations/0005_auto_20210203_2142.py
+++ b/qgis-app/geopackages/migrations/0005_auto_20210203_2142.py
@@ -1,20 +1,26 @@
# Generated by Django 2.2.13 on 2021-02-03 21:42
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
- ('geopackages', '0004_auto_20210121_0227'),
+ ("geopackages", "0004_auto_20210121_0227"),
]
operations = [
migrations.AlterField(
- model_name='review',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='geopackages_review_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="review",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this %(app_label)s.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="geopackages_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/geopackages/migrations/0006_add_uuid_field.py b/qgis-app/geopackages/migrations/0006_add_uuid_field.py
index d1aca14f..cdd4ac4f 100644
--- a/qgis-app/geopackages/migrations/0006_add_uuid_field.py
+++ b/qgis-app/geopackages/migrations/0006_add_uuid_field.py
@@ -1,19 +1,20 @@
# Generated by Django 2.2.13 on 2021-02-10 19:06
-from django.db import migrations, models
import uuid
+from django.db import migrations, models
+
class Migration(migrations.Migration):
dependencies = [
- ('geopackages', '0005_auto_20210203_2142'),
+ ("geopackages", "0005_auto_20210203_2142"),
]
operations = [
migrations.AddField(
- model_name='geopackage',
- name='uuid',
+ model_name="geopackage",
+ name="uuid",
field=models.UUIDField(default=uuid.uuid4, null=True),
),
]
diff --git a/qgis-app/geopackages/migrations/0007_populate_uuid_value.py b/qgis-app/geopackages/migrations/0007_populate_uuid_value.py
index 2331a1ec..9a39a3d7 100644
--- a/qgis-app/geopackages/migrations/0007_populate_uuid_value.py
+++ b/qgis-app/geopackages/migrations/0007_populate_uuid_value.py
@@ -1,20 +1,21 @@
# Generated by Django 2.2.13 on 2021-02-10 19:06
-from django.db import migrations
import uuid
+from django.db import migrations
+
def gen_uuid(apps, schema_editor):
- Geopackage = apps.get_model('geopackages', 'Geopackage')
+ Geopackage = apps.get_model("geopackages", "Geopackage")
for row in Geopackage.objects.all():
row.uuid = uuid.uuid4()
- row.save(update_fields=['uuid'])
+ row.save(update_fields=["uuid"])
class Migration(migrations.Migration):
dependencies = [
- ('geopackages', '0006_add_uuid_field'),
+ ("geopackages", "0006_add_uuid_field"),
]
operations = [
diff --git a/qgis-app/geopackages/migrations/0008_remove_uuid_null.py b/qgis-app/geopackages/migrations/0008_remove_uuid_null.py
index ee8e2814..795fa3e8 100644
--- a/qgis-app/geopackages/migrations/0008_remove_uuid_null.py
+++ b/qgis-app/geopackages/migrations/0008_remove_uuid_null.py
@@ -1,19 +1,20 @@
# Generated by Django 2.2.13 on 2021-02-10 19:06
-from django.db import migrations, models
import uuid
+from django.db import migrations, models
+
class Migration(migrations.Migration):
dependencies = [
- ('geopackages', '0007_populate_uuid_value'),
+ ("geopackages", "0007_populate_uuid_value"),
]
operations = [
migrations.AlterField(
- model_name='geopackage',
- name='uuid',
+ model_name="geopackage",
+ name="uuid",
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
diff --git a/qgis-app/geopackages/models.py b/qgis-app/geopackages/models.py
index 26a800b3..0cfc14e2 100644
--- a/qgis-app/geopackages/models.py
+++ b/qgis-app/geopackages/models.py
@@ -1,38 +1,39 @@
import os
+from base.models.processing_models import Resource, ResourceReview
from django.conf import settings
from django.core.validators import FileExtensionValidator
from django.db import models
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
-from base.models.processing_models import Resource, ResourceReview
-
-GEOPACKAGES_STORAGE_PATH = getattr(settings,
- 'GEOPACKAGE_STORAGE_PATH', 'geopackages/%Y')
+GEOPACKAGES_STORAGE_PATH = getattr(
+ settings, "GEOPACKAGE_STORAGE_PATH", "geopackages/%Y"
+)
class Geopackage(Resource):
# file
file = models.FileField(
- _('GeoPackage file'),
- help_text=_('A GeoPackage file. The filesize must less than 1MB '),
+ _("GeoPackage file"),
+ help_text=_("A GeoPackage file. The filesize must less than 1MB "),
upload_to=GEOPACKAGES_STORAGE_PATH,
- validators=[FileExtensionValidator(
- allowed_extensions=['gpkg', 'zip'])],
- null=False)
+ validators=[FileExtensionValidator(allowed_extensions=["gpkg", "zip"])],
+ null=False,
+ )
# thumbnail
thumbnail_image = models.ImageField(
- _('Thumbnail'),
- help_text=_('Please upload an image that demonstrate this resource.'),
+ _("Thumbnail"),
+ help_text=_("Please upload an image that demonstrate this resource."),
blank=False,
null=False,
- upload_to=GEOPACKAGES_STORAGE_PATH)
+ upload_to=GEOPACKAGES_STORAGE_PATH,
+ )
def get_absolute_url(self):
- return reverse('geopackage_detail', args=(self.id,))
+ return reverse("geopackage_detail", args=(self.id,))
def extension(self):
name, extension = os.path.splitext(self.file.name)
@@ -44,8 +45,9 @@ class Review(ResourceReview):
# Geopackage resource
resource = models.ForeignKey(
Geopackage,
- verbose_name=_('GeoPackage'),
- help_text=_('The reviewed GeoPackage'),
+ verbose_name=_("GeoPackage"),
+ help_text=_("The reviewed GeoPackage"),
blank=False,
null=False,
- on_delete=models.CASCADE)
+ on_delete=models.CASCADE,
+ )
diff --git a/qgis-app/geopackages/tests/gpkgfiles/geopackages/2020/spiky_polygons.gpkg b/qgis-app/geopackages/tests/gpkgfiles/geopackages/2020/spiky_polygons.gpkg
index 8773f398..dd59d098 100644
--- a/qgis-app/geopackages/tests/gpkgfiles/geopackages/2020/spiky_polygons.gpkg
+++ b/qgis-app/geopackages/tests/gpkgfiles/geopackages/2020/spiky_polygons.gpkg
@@ -1 +1 @@
-file content
\ No newline at end of file
+file content
diff --git a/qgis-app/geopackages/tests/test_views.py b/qgis-app/geopackages/tests/test_views.py
index da65f8f6..3521dd19 100644
--- a/qgis-app/geopackages/tests/test_views.py
+++ b/qgis-app/geopackages/tests/test_views.py
@@ -1,33 +1,32 @@
import os
import tempfile
+from base.views.processing_view import resource_notify, resource_update_notify
+from django.contrib.auth.models import Group, User
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse
-from django.contrib.auth.models import User, Group
-from geopackages.models import Geopackage, Review
-
-from base.views.processing_view import resource_notify, resource_update_notify
from geopackages.forms import UploadForm
+from geopackages.models import Geopackage, Review
GPKG_DIR = os.path.join(os.path.dirname(__file__), "gpkgfiles")
-class SetUpTest():
+class SetUpTest:
"""
SetUp for all Test Class
"""
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
self.thumbnail = os.path.join(GPKG_DIR, "thumbnail.png")
- self.thumbnail_content = open(self.thumbnail, 'rb')
+ self.thumbnail_content = open(self.thumbnail, "rb")
self.file = os.path.join(GPKG_DIR, "spiky-polygons.gpkg")
- self.file_content = open(self.file, 'rb')
+ self.file_content = open(self.file, "rb")
self.gpkg_oversize = os.path.join(GPKG_DIR, "dummy_oversize.gpkg")
- self.gpkg_oversize_content = open(self.gpkg_oversize, 'rb')
+ self.gpkg_oversize_content = open(self.gpkg_oversize, "rb")
self.creator = User.objects.create(
username="creator", email="creator@email.com"
@@ -35,9 +34,7 @@ def setUp(self):
# set creator password to password
self.creator.set_password("password")
self.creator.save()
- self.staff = User.objects.create(
- username="staff", email="staff@email.com"
- )
+ self.staff = User.objects.create(username="staff", email="staff@email.com")
self.staff.set_password("password")
self.staff.save()
self.group = Group.objects.create(name="Style Managers")
@@ -51,74 +48,54 @@ def tearDown(self):
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class TestFormValidation(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def test_form_with_valid_data(self):
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_gpkg = SimpleUploadedFile(
- self.file_content.name,
- self.file_content.read()
+ self.file_content.name, self.file_content.read()
)
form = UploadForm(data={})
self.assertFalse(form.is_valid())
- data = {
- "name": "spiky polygons",
- "description": "Test upload with valid data"
- }
- file_data = {
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_gpkg
- }
+ data = {"name": "spiky polygons", "description": "Test upload with valid data"}
+ file_data = {"thumbnail_image": uploaded_thumbnail, "file": uploaded_gpkg}
form = UploadForm(data, file_data)
self.assertTrue(form.is_valid())
def test_form_invalid_file_extension(self):
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_gpkg = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
data = {
- "name": "spiky polygons",
- "description": "Test upload invalid gpkg file extension"
- }
- file_data = {
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_gpkg
+ "name": "spiky polygons",
+ "description": "Test upload invalid gpkg file extension",
}
+ file_data = {"thumbnail_image": uploaded_thumbnail, "file": uploaded_gpkg}
form = UploadForm(data, file_data)
self.assertFalse(form.is_valid())
- self.assertEqual(form.errors,
- {'file': ['The submitted file is empty.']})
+ self.assertEqual(form.errors, {"file": ["The submitted file is empty."]})
def test_form_invalid_filesize(self):
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_gpkg = SimpleUploadedFile(
- self.gpkg_oversize_content.name,
- self.gpkg_oversize_content.read()
+ self.gpkg_oversize_content.name, self.gpkg_oversize_content.read()
)
data = {
- 'name': 'spiky polygons',
- 'description': 'Test upload invalid gpkg filesize'
- }
- file_data = {
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_gpkg
+ "name": "spiky polygons",
+ "description": "Test upload invalid gpkg filesize",
}
+ file_data = {"thumbnail_image": uploaded_thumbnail, "file": uploaded_gpkg}
form = UploadForm(data, file_data)
self.assertFalse(form.is_valid())
self.assertEqual(
- form.errors,
- {'file': ['File is too big. Max size is 1.0 Megabytes']}
+ form.errors, {"file": ["File is too big. Max size is 1.0 Megabytes"]}
)
@@ -128,68 +105,68 @@ class TestEmailNotification(SetUpTest, TestCase):
Send the email to console
"""
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
- @override_settings(
- EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend')
+ @override_settings(EMAIL_BACKEND="django.core.mail.backends.console.EmailBackend")
def test_print_email_notification_in_console(self):
Geopackage.objects.create(
creator=self.creator,
name="spiky polygons",
description="A GeoPackage for testing purpose",
thumbnail_image=self.thumbnail,
- file=self.file
+ file=self.file,
)
gpkg = Geopackage.objects.first()
- resource_notify(gpkg, resource_type='GeoPackage')
+ resource_notify(gpkg, resource_type="GeoPackage")
Review.objects.create(
- reviewer=self.staff,
- resource=gpkg,
- comment="Rejected for testing purpose")
+ reviewer=self.staff, resource=gpkg, comment="Rejected for testing purpose"
+ )
gpkg.require_action = True
gpkg.save()
- resource_update_notify(gpkg, self.creator, self.staff,
- resource_type='GeoPackage')
+ resource_update_notify(
+ gpkg, self.creator, self.staff, resource_type="GeoPackage"
+ )
Review.objects.create(
reviewer=self.staff,
resource=gpkg,
- comment="Approved! This is for testing purpose")
+ comment="Approved! This is for testing purpose",
+ )
gpkg.approved = True
gpkg.save()
- resource_update_notify(gpkg, self.creator, self.staff,
- resource_type='GeoPackage')
+ resource_update_notify(
+ gpkg, self.creator, self.staff, resource_type="GeoPackage"
+ )
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class TestUploadGeoPackage(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def test_upload_acceptable_size_file(self):
login = self.client.login(username="creator", password="password")
self.assertTrue(login)
url = reverse("geopackage_create")
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_gpkg = SimpleUploadedFile(
- self.file_content.name,
- self.file_content.read()
+ self.file_content.name, self.file_content.read()
)
data = {
"name": "spiky polygons",
"description": "Test upload an acceptable gpkg size",
"thumbnail_image": uploaded_thumbnail,
- "file": uploaded_gpkg
+ "file": uploaded_gpkg,
}
response = self.client.post(url, data, follow=True)
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].subject,
- "A new GeoPackage has been created by creator.")
+ self.assertEqual(
+ mail.outbox[0].subject, "A new GeoPackage has been created by creator."
+ )
gpkg = Geopackage.objects.first()
self.assertEqual(gpkg.name, "spiky polygons")
- url = reverse("geopackage_detail", kwargs={'pk': gpkg.id})
+ url = reverse("geopackage_detail", kwargs={"pk": gpkg.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -198,18 +175,16 @@ def test_upload_invalid_size_file(self):
self.assertTrue(login)
url = reverse("geopackage_create")
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_gpkg = SimpleUploadedFile(
- self.gpkg_oversize_content.name,
- self.gpkg_oversize_content.read()
+ self.gpkg_oversize_content.name, self.gpkg_oversize_content.read()
)
data = {
"name": "spiky polygons",
"description": "Test upload a gpkg > 1Mb filesize",
"thumbnail_image": uploaded_thumbnail,
- "file": uploaded_gpkg
+ "file": uploaded_gpkg,
}
response = self.client.post(url, data, follow=True)
self.assertEqual(response.status_code, 200)
@@ -220,7 +195,7 @@ def test_upload_invalid_size_file(self):
@override_settings(MEDIA_ROOT="geopackages/tests/gpkgfiles/")
class TestReviewGeopackage(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
super(TestReviewGeopackage, self).setUp()
@@ -229,48 +204,49 @@ def setUp(self):
name="spiky polygons",
description="A GeoPackage for testing purpose",
thumbnail_image=self.thumbnail,
- file=self.file
+ file=self.file,
)
def test_approve_gpkg(self):
login = self.client.login(username="staff", password="password")
self.assertTrue(login)
- url = reverse('geopackage_review', kwargs={'pk': self.gpkg_object.id})
- response = self.client.post(url, {
- 'approval': 'approve',
- 'comment': 'This should be in Approve page.'
- })
+ url = reverse("geopackage_review", kwargs={"pk": self.gpkg_object.id})
+ response = self.client.post(
+ url, {"approval": "approve", "comment": "This should be in Approve page."}
+ )
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- url = reverse('geopackage_detail', kwargs={'pk': self.gpkg_object.id})
+ url = reverse("geopackage_detail", kwargs={"pk": self.gpkg_object.id})
self.assertRedirects(response, url)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertNotContains(response, 'This should be in Approve page.')
- self.assertContains(response, 'Approved Date')
+ self.assertNotContains(response, "This should be in Approve page.")
+ self.assertContains(response, "Approved Date")
self.client.logout()
def test_reject_gpkg(self):
login = self.client.login(username="staff", password="password")
self.assertTrue(login)
- url = reverse('geopackage_review', kwargs={'pk': self.gpkg_object.id})
- response = self.client.post(url, {
- 'approval': 'reject',
- 'comment': 'This should be in requiring update page.'
- })
+ url = reverse("geopackage_review", kwargs={"pk": self.gpkg_object.id})
+ response = self.client.post(
+ url,
+ {
+ "approval": "reject",
+ "comment": "This should be in requiring update page.",
+ },
+ )
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- url = reverse('geopackage_detail', kwargs={'pk': self.gpkg_object.id})
+ url = reverse("geopackage_detail", kwargs={"pk": self.gpkg_object.id})
self.assertRedirects(response, url)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertContains(response,
- 'This should be in requiring update page.')
- self.assertContains(response, 'Reviewed by Staff now')
+ self.assertContains(response, "This should be in requiring update page.")
+ self.assertContains(response, "Reviewed by Staff now")
self.client.logout()
# creator should find the rejected styles in requiring update page
self.client.login(username="creator", password="password")
- url = reverse('geopackage_require_action')
+ url = reverse("geopackage_require_action")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "1 record found.")
diff --git a/qgis-app/geopackages/urls.py b/qgis-app/geopackages/urls.py
index 3fdb932f..78e14616 100644
--- a/qgis-app/geopackages/urls.py
+++ b/qgis-app/geopackages/urls.py
@@ -1,37 +1,40 @@
from django.urls import path
-
-from geopackages.views import (GeopackageCreateView,
- GeopackageDetailView,
- GeopackageUpdateView,
- GeopackageListView,
- GeopackageDeleteView,
- GeopackageUnapprovedListView,
- GeopackageRequireActionListView,
- GeopackageReviewView,
- GeopackageDownloadView,
- geopackage_nav_content)
-
+from geopackages.views import (
+ GeopackageCreateView,
+ GeopackageDeleteView,
+ GeopackageDetailView,
+ GeopackageDownloadView,
+ GeopackageListView,
+ GeopackageRequireActionListView,
+ GeopackageReviewView,
+ GeopackageUnapprovedListView,
+ GeopackageUpdateView,
+ geopackage_nav_content,
+)
urlpatterns = [
# GeoPackage
- path('', GeopackageListView.as_view(), name='geopackage_list'),
- path('add/', GeopackageCreateView.as_view(), name='geopackage_create'),
- path('/', GeopackageDetailView.as_view(),
- name='geopackage_detail'),
- path('/update/', GeopackageUpdateView.as_view(),
- name='geopackage_update'),
- path('/delete/', GeopackageDeleteView.as_view(),
- name='geopackage_delete'),
- path('/review/', GeopackageReviewView.as_view(),
- name='geopackage_review'),
- path('/download/', GeopackageDownloadView.as_view(),
- name='geopackage_download'),
-
- path('unapproved/', GeopackageUnapprovedListView.as_view(),
- name='geopackage_unapproved'),
- path('require_action/', GeopackageRequireActionListView.as_view(),
- name='geopackage_require_action'),
-
+ path("", GeopackageListView.as_view(), name="geopackage_list"),
+ path("add/", GeopackageCreateView.as_view(), name="geopackage_create"),
+ path("/", GeopackageDetailView.as_view(), name="geopackage_detail"),
+ path("/update/", GeopackageUpdateView.as_view(), name="geopackage_update"),
+ path("/delete/", GeopackageDeleteView.as_view(), name="geopackage_delete"),
+ path("/review/", GeopackageReviewView.as_view(), name="geopackage_review"),
+ path(
+ "/download/",
+ GeopackageDownloadView.as_view(),
+ name="geopackage_download",
+ ),
+ path(
+ "unapproved/",
+ GeopackageUnapprovedListView.as_view(),
+ name="geopackage_unapproved",
+ ),
+ path(
+ "require_action/",
+ GeopackageRequireActionListView.as_view(),
+ name="geopackage_require_action",
+ ),
# JSON
- path('sidebarnav/', geopackage_nav_content, name="geopackage_nav_content"),
+ path("sidebarnav/", geopackage_nav_content, name="geopackage_nav_content"),
]
diff --git a/qgis-app/geopackages/views.py b/qgis-app/geopackages/views.py
index d7d46164..e92632a3 100644
--- a/qgis-app/geopackages/views.py
+++ b/qgis-app/geopackages/views.py
@@ -1,19 +1,20 @@
-from base.views.processing_view import (ResourceBaseCreateView,
- ResourceBaseDetailView,
- ResourceBaseUpdateView,
- ResourceBaseListView,
- ResourceBaseUnapprovedListView,
- ResourceBaseRequireActionListView,
- ResourceBaseDeleteView,
- ResourceBaseReviewView,
- ResourceBaseDownload,
- resource_nav_content)
-
-from geopackages.forms import (UpdateForm, UploadForm,)
+from base.views.processing_view import (
+ ResourceBaseCreateView,
+ ResourceBaseDeleteView,
+ ResourceBaseDetailView,
+ ResourceBaseDownload,
+ ResourceBaseListView,
+ ResourceBaseRequireActionListView,
+ ResourceBaseReviewView,
+ ResourceBaseUnapprovedListView,
+ ResourceBaseUpdateView,
+ resource_nav_content,
+)
+from geopackages.forms import UpdateForm, UploadForm
from geopackages.models import Geopackage, Review
-class ResourceMixin():
+class ResourceMixin:
"""Mixin class for Geopackage."""
model = Geopackage
@@ -21,10 +22,10 @@ class ResourceMixin():
review_model = Review
# The resource_name will be displayed as the app name on web page
- resource_name = 'GeoPackage'
+ resource_name = "GeoPackage"
# The url name in urls.py should start start with this value
- resource_name_url_base = 'geopackage'
+ resource_name_url_base = "geopackage"
class GeopackageCreateView(ResourceMixin, ResourceBaseCreateView):
@@ -47,13 +48,11 @@ class GeopackageListView(ResourceMixin, ResourceBaseListView):
"""Approved GeoPackage ListView"""
-class GeopackageUnapprovedListView(ResourceMixin,
- ResourceBaseUnapprovedListView):
+class GeopackageUnapprovedListView(ResourceMixin, ResourceBaseUnapprovedListView):
"""Unapproved GeoPackage ListView"""
-class GeopackageRequireActionListView(ResourceMixin,
- ResourceBaseRequireActionListView):
+class GeopackageRequireActionListView(ResourceMixin, ResourceBaseRequireActionListView):
"""Geopackage Requires Action"""
diff --git a/qgis-app/homepage.py b/qgis-app/homepage.py
index ee571ce2..7bf3d7c2 100644
--- a/qgis-app/homepage.py
+++ b/qgis-app/homepage.py
@@ -1,9 +1,10 @@
+from django.contrib.flatpages.models import FlatPage
from django.shortcuts import render
from django.template import RequestContext
-from plugins.models import Plugin
-from django.contrib.flatpages.models import FlatPage
from django.utils.translation import ugettext_lazy as _
-#from feedjack.models import Post
+from plugins.models import Plugin
+
+# from feedjack.models import Post
def homepage(request):
@@ -14,14 +15,17 @@ def homepage(request):
featured = Plugin.featured_objects.all()[:10]
popular = Plugin.popular_objects.all()[:10]
try:
- content = FlatPage.objects.get(url='/').content
+ content = FlatPage.objects.get(url="/").content
except FlatPage.DoesNotExist:
content = _('To add content here, create a FlatPage with url="/"')
- return render(request, 'flatpages/homepage.html', {
- 'featured' : featured,
- 'latest' : latest,
- 'popular' : popular,
- 'content' : content,
- })
-
+ return render(
+ request,
+ "flatpages/homepage.html",
+ {
+ "featured": featured,
+ "latest": latest,
+ "popular": popular,
+ "content": content,
+ },
+ )
diff --git a/qgis-app/layerdefinitions/admin.py b/qgis-app/layerdefinitions/admin.py
index 7a442b17..2c39f550 100644
--- a/qgis-app/layerdefinitions/admin.py
+++ b/qgis-app/layerdefinitions/admin.py
@@ -4,16 +4,28 @@
class LayerDefinitionInline(admin.TabularInline):
model = Review
- list_display = ('review_date', 'comment', 'reviewer')
+ list_display = ("review_date", "comment", "reviewer")
@admin.register(LayerDefinition)
class LayerDefinitionAdmin(admin.ModelAdmin):
- inlines = [LayerDefinitionInline, ]
- list_display = ('name', 'description', 'creator', 'upload_date',)
- search_fields = ('name', 'description', 'provider')
+ inlines = [
+ LayerDefinitionInline,
+ ]
+ list_display = (
+ "name",
+ "description",
+ "creator",
+ "upload_date",
+ )
+ search_fields = ("name", "description", "provider")
@admin.register(Review)
class LayerDefinitionReviewAdmin(admin.ModelAdmin):
- list_display = ('resource', 'reviewer', 'comment', 'review_date',)
+ list_display = (
+ "resource",
+ "reviewer",
+ "comment",
+ "review_date",
+ )
diff --git a/qgis-app/layerdefinitions/apps.py b/qgis-app/layerdefinitions/apps.py
index 35609e31..d936881a 100644
--- a/qgis-app/layerdefinitions/apps.py
+++ b/qgis-app/layerdefinitions/apps.py
@@ -2,4 +2,4 @@
class LayerdefinitionsConfig(AppConfig):
- name = 'layerdefinitions'
+ name = "layerdefinitions"
diff --git a/qgis-app/layerdefinitions/file_handler.py b/qgis-app/layerdefinitions/file_handler.py
index 3e4bd02d..ddacc02d 100644
--- a/qgis-app/layerdefinitions/file_handler.py
+++ b/qgis-app/layerdefinitions/file_handler.py
@@ -14,24 +14,26 @@ def parse_qlr(xmlfile):
try:
tree = ET.parse(xmlfile)
except ET.ParseError:
- raise ValidationError(_('Cannot parse the qlr file. '
- 'Please ensure your file is correct.'))
+ raise ValidationError(
+ _("Cannot parse the qlr file. " "Please ensure your file is correct.")
+ )
return tree
def validator(xmlfile):
tree = parse_qlr(xmlfile)
root = tree.getroot()
- if not root or not root.tag == 'qlr':
- raise ValidationError(_('Invalid root tag of qlr file. '
- 'Please ensure your file is correct.'))
+ if not root or not root.tag == "qlr":
+ raise ValidationError(
+ _("Invalid root tag of qlr file. " "Please ensure your file is correct.")
+ )
return True
def get_url_datasource(xmlfile):
tree = parse_qlr(xmlfile)
root = tree.getroot()
- datasource = root.find('./maplayers/maplayer/datasource')
+ datasource = root.find("./maplayers/maplayer/datasource")
rgx = r'(?<=url=)[\'"]?(.*?)[\'"&\s]*?$'
try:
url = re.findall(rgx, datasource.text)
@@ -46,7 +48,7 @@ def get_url_datasource(xmlfile):
def get_provider(xmlfile):
tree = parse_qlr(xmlfile)
root = tree.getroot()
- provider = root.find('./maplayers/maplayer/provider')
+ provider = root.find("./maplayers/maplayer/provider")
try:
return provider.text
except AttributeError:
diff --git a/qgis-app/layerdefinitions/forms.py b/qgis-app/layerdefinitions/forms.py
index 05264aab..c4954efd 100644
--- a/qgis-app/layerdefinitions/forms.py
+++ b/qgis-app/layerdefinitions/forms.py
@@ -1,22 +1,25 @@
+from base.forms.processing_forms import ResourceBaseCleanFileForm
from django import forms
-
-from layerdefinitions.models import LayerDefinition
from layerdefinitions.file_handler import validator
-
-from base.forms.processing_forms import ResourceBaseCleanFileForm
+from layerdefinitions.models import LayerDefinition
class ResourceFormMixin(forms.ModelForm):
class Meta:
model = LayerDefinition
fields = [
- 'file', 'thumbnail_image', 'name', 'url_metadata',
- 'description', 'license',
+ "file",
+ "thumbnail_image",
+ "name",
+ "url_metadata",
+ "description",
+ "license",
]
class UploadForm(ResourceBaseCleanFileForm, ResourceFormMixin):
"""Upload Form."""
+
def clean_file(self):
file = super(UploadForm, self).clean_file()
validator(file)
diff --git a/qgis-app/layerdefinitions/license.py b/qgis-app/layerdefinitions/license.py
index f337e33a..98387f7b 100644
--- a/qgis-app/layerdefinitions/license.py
+++ b/qgis-app/layerdefinitions/license.py
@@ -6,10 +6,10 @@
def create_license_file(string):
- tmp_dir = f'/tmp/{uuid.uuid4().hex}'
- file = f'{tmp_dir}/license.txt'
+ tmp_dir = f"/tmp/{uuid.uuid4().hex}"
+ file = f"{tmp_dir}/license.txt"
os.mkdir(tmp_dir)
- with open(file, 'w') as f:
+ with open(file, "w") as f:
f.write(string)
f.seek(0)
return file, tmp_dir
diff --git a/qgis-app/layerdefinitions/migrations/0001_initial.py b/qgis-app/layerdefinitions/migrations/0001_initial.py
index 44946840..f571a1c1 100644
--- a/qgis-app/layerdefinitions/migrations/0001_initial.py
+++ b/qgis-app/layerdefinitions/migrations/0001_initial.py
@@ -1,10 +1,11 @@
# Generated by Django 2.2.24 on 2021-11-29 18:32
-from django.conf import settings
+import uuid
+
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
-import uuid
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -17,41 +18,197 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
- name='LayerDefinition',
+ name="LayerDefinition",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.UUIDField(default=uuid.uuid4, unique=True)),
- ('upload_date', models.DateTimeField(auto_now_add=True, help_text='The upload date. Automatically added on file upload.', verbose_name='Uploaded on')),
- ('modified_date', models.DateTimeField(editable=False, help_text='The upload date. Automatically added on file upload.', verbose_name='Modified on')),
- ('name', models.CharField(help_text='A unique name for this resource', max_length=256, unique=True, verbose_name='Name')),
- ('description', models.TextField(help_text='A description of this resource.', max_length=5000, verbose_name='Description')),
- ('download_count', models.IntegerField(default=0, editable=False, help_text='The number of times this resource has been downloaded. This is updated automatically.', verbose_name='Downloads')),
- ('approved', models.BooleanField(db_index=True, default=False, help_text='Set to True if you wish to approve this resource.', verbose_name='Approved')),
- ('require_action', models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update the resource.', verbose_name='Requires Action')),
- ('file', models.FileField(help_text='A Layer Definition file. The filesize must less than 1MB', upload_to='layerdefinitions', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['qlr'])], verbose_name='Layer Definition file')),
- ('thumbnail_image', models.ImageField(help_text='Please upload an image that demonstrate this resource.', upload_to='layerdefinitions', verbose_name='Thumbnail')),
- ('url_datasource', models.URLField(blank=True, null=True, verbose_name='URL Data Source.')),
- ('url_metadata', models.URLField(blank=True, help_text='Please add a URL where we can find metadata information of this resource.', null=True, verbose_name='URL Metadata')),
- ('provider', models.TextField(blank=True, max_length=100, null=True, verbose_name='Provider')),
- ('license', models.TextField(blank=True, help_text='License of this resource.', max_length=500, null=True, verbose_name='License')),
- ('creator', models.ForeignKey(help_text='The user who uploaded this resource.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("uuid", models.UUIDField(default=uuid.uuid4, unique=True)),
+ (
+ "upload_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Uploaded on",
+ ),
+ ),
+ (
+ "modified_date",
+ models.DateTimeField(
+ editable=False,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Modified on",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ help_text="A unique name for this resource",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
+ ),
+ (
+ "description",
+ models.TextField(
+ help_text="A description of this resource.",
+ max_length=5000,
+ verbose_name="Description",
+ ),
+ ),
+ (
+ "download_count",
+ models.IntegerField(
+ default=0,
+ editable=False,
+ help_text="The number of times this resource has been downloaded. This is updated automatically.",
+ verbose_name="Downloads",
+ ),
+ ),
+ (
+ "approved",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you wish to approve this resource.",
+ verbose_name="Approved",
+ ),
+ ),
+ (
+ "require_action",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update the resource.",
+ verbose_name="Requires Action",
+ ),
+ ),
+ (
+ "file",
+ models.FileField(
+ help_text="A Layer Definition file. The filesize must less than 1MB",
+ upload_to="layerdefinitions",
+ validators=[
+ django.core.validators.FileExtensionValidator(
+ allowed_extensions=["qlr"]
+ )
+ ],
+ verbose_name="Layer Definition file",
+ ),
+ ),
+ (
+ "thumbnail_image",
+ models.ImageField(
+ help_text="Please upload an image that demonstrate this resource.",
+ upload_to="layerdefinitions",
+ verbose_name="Thumbnail",
+ ),
+ ),
+ (
+ "url_datasource",
+ models.URLField(
+ blank=True, null=True, verbose_name="URL Data Source."
+ ),
+ ),
+ (
+ "url_metadata",
+ models.URLField(
+ blank=True,
+ help_text="Please add a URL where we can find metadata information of this resource.",
+ null=True,
+ verbose_name="URL Metadata",
+ ),
+ ),
+ (
+ "provider",
+ models.TextField(
+ blank=True, max_length=100, null=True, verbose_name="Provider"
+ ),
+ ),
+ (
+ "license",
+ models.TextField(
+ blank=True,
+ help_text="License of this resource.",
+ max_length=500,
+ null=True,
+ verbose_name="License",
+ ),
+ ),
+ (
+ "creator",
+ models.ForeignKey(
+ help_text="The user who uploaded this resource.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
+ ),
],
options={
- 'abstract': False,
+ "abstract": False,
},
),
migrations.CreateModel(
- name='Review',
+ name="Review",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('review_date', models.DateTimeField(auto_now_add=True, help_text='The review date. Automatically added on review resource.', verbose_name='Reviewed on')),
- ('comment', models.TextField(blank=True, help_text='A review comment. Please write your review.', max_length=1000, null=True, verbose_name='Comment')),
- ('resource', models.ForeignKey(help_text='The reviewed Layer Definition.', on_delete=django.db.models.deletion.CASCADE, to='layerdefinitions.LayerDefinition', verbose_name='Layer Definition')),
- ('reviewer', models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='layerdefinitions_review_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "review_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The review date. Automatically added on review resource.",
+ verbose_name="Reviewed on",
+ ),
+ ),
+ (
+ "comment",
+ models.TextField(
+ blank=True,
+ help_text="A review comment. Please write your review.",
+ max_length=1000,
+ null=True,
+ verbose_name="Comment",
+ ),
+ ),
+ (
+ "resource",
+ models.ForeignKey(
+ help_text="The reviewed Layer Definition.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to="layerdefinitions.LayerDefinition",
+ verbose_name="Layer Definition",
+ ),
+ ),
+ (
+ "reviewer",
+ models.ForeignKey(
+ help_text="The user who reviewed this %(app_label)s.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="layerdefinitions_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
+ ),
],
options={
- 'ordering': ['review_date'],
- 'abstract': False,
+ "ordering": ["review_date"],
+ "abstract": False,
},
),
]
diff --git a/qgis-app/layerdefinitions/models.py b/qgis-app/layerdefinitions/models.py
index b4f4e7d8..1c6be6e4 100644
--- a/qgis-app/layerdefinitions/models.py
+++ b/qgis-app/layerdefinitions/models.py
@@ -1,15 +1,15 @@
import os
+from base.models.processing_models import Resource, ResourceReview
from django.conf import settings
from django.core.validators import FileExtensionValidator
from django.db import models
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
-from base.models.processing_models import Resource, ResourceReview
-
LAYERDEFINITIONS_STORAGE_PATH = getattr(
- settings, 'LAYERDEFINITION_STORAGE_PATH', 'layerdefinitions')
+ settings, "LAYERDEFINITION_STORAGE_PATH", "layerdefinitions"
+)
class LayerDefinition(Resource):
@@ -17,56 +17,53 @@ class LayerDefinition(Resource):
# file
file = models.FileField(
- _('Layer Definition file'),
- help_text=_('A Layer Definition file. '
- 'The filesize must less than 1MB'),
+ _("Layer Definition file"),
+ help_text=_("A Layer Definition file. " "The filesize must less than 1MB"),
upload_to=LAYERDEFINITIONS_STORAGE_PATH,
- validators=[FileExtensionValidator(allowed_extensions=['qlr'])],
- null=False)
+ validators=[FileExtensionValidator(allowed_extensions=["qlr"])],
+ null=False,
+ )
# thumbnail
thumbnail_image = models.ImageField(
- _('Thumbnail'),
- help_text=_('Please upload an image that demonstrate this resource.'),
+ _("Thumbnail"),
+ help_text=_("Please upload an image that demonstrate this resource."),
blank=False,
null=False,
- upload_to=LAYERDEFINITIONS_STORAGE_PATH)
+ upload_to=LAYERDEFINITIONS_STORAGE_PATH,
+ )
# url datasource
url_datasource = models.URLField(
- _('URL Data Source.'),
- blank=True,
- null=True,
- max_length=200)
+ _("URL Data Source."), blank=True, null=True, max_length=200
+ )
# url metadata
url_metadata = models.URLField(
- _('URL Metadata'),
- help_text=_('Please add a URL where we can find metadata information '
- 'of this resource.'),
+ _("URL Metadata"),
+ help_text=_(
+ "Please add a URL where we can find metadata information "
+ "of this resource."
+ ),
blank=True,
null=True,
- max_length=200)
+ max_length=200,
+ )
# provider
- provider = models.TextField(
- _('Provider'),
- max_length=100,
- blank=True,
- null=True
- )
+ provider = models.TextField(_("Provider"), max_length=100, blank=True, null=True)
# license
license = models.TextField(
- _('License'),
- help_text=_('License of this resource.'),
+ _("License"),
+ help_text=_("License of this resource."),
max_length=500,
blank=True,
- null=True
+ null=True,
)
def get_absolute_url(self):
- return reverse('layerdefinition_detail', args=(self.id,))
+ return reverse("layerdefinition_detail", args=(self.id,))
def extension(self):
name, extension = os.path.splitext(self.file.name)
@@ -78,8 +75,9 @@ class Review(ResourceReview):
# Layer Definition resource
resource = models.ForeignKey(
LayerDefinition,
- verbose_name=_('Layer Definition'),
- help_text=_('The reviewed Layer Definition.'),
+ verbose_name=_("Layer Definition"),
+ help_text=_("The reviewed Layer Definition."),
blank=False,
null=False,
- on_delete=models.CASCADE)
+ on_delete=models.CASCADE,
+ )
diff --git a/qgis-app/layerdefinitions/tests/test_file_handler.py b/qgis-app/layerdefinitions/tests/test_file_handler.py
index b51834ff..c2439d1e 100644
--- a/qgis-app/layerdefinitions/tests/test_file_handler.py
+++ b/qgis-app/layerdefinitions/tests/test_file_handler.py
@@ -3,21 +3,21 @@
from django.core.exceptions import ValidationError
from django.test import TestCase
+from layerdefinitions.file_handler import (
+ get_provider,
+ get_url_datasource,
+ parse_qlr,
+ validator,
+)
-from layerdefinitions.file_handler import (parse_qlr,
- validator,
- get_url_datasource,
- get_provider)
+TESTFILE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "testfiles"))
-TESTFILE_DIR = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'testfiles'))
-
-class SetUpTest():
+class SetUpTest:
"""Setup for all test class."""
+
def setUp(self):
- self.qlr_file = os.path.join(
- TESTFILE_DIR, "my-vapour-pressure.qlr")
+ self.qlr_file = os.path.join(TESTFILE_DIR, "my-vapour-pressure.qlr")
class TestParseQlr(SetUpTest, TestCase):
@@ -28,7 +28,7 @@ def test_parse_qlr(self):
self.assertTrue(parse_qlr(f))
def test_parse_qlr_should_failed(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".qlr")
+ tf = NamedTemporaryFile(mode="w+t", suffix=".qlr")
tf.write("")
- msg = ("Invalid root tag of qlr file. "
- "Please ensure your file is correct.")
+ msg = "Invalid root tag of qlr file. " "Please ensure your file is correct."
with self.assertRaisesMessage(ValidationError, msg):
validator(tf)
@@ -57,24 +56,25 @@ class TestGetUrlDatasource(SetUpTest, TestCase):
def test_get_url_datasource_should_succeed(self):
with open(self.qlr_file) as f:
self.assertEqual(
- get_url_datasource(f),
- "https://maps.kartoza.com/geoserver/kartoza/wms"
+ get_url_datasource(f), "https://maps.kartoza.com/geoserver/kartoza/wms"
)
def test_get_url_datasource_no_content_should_return_none(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".qlr")
- tf.write(""
- ""
- ""
- )
+ tf = NamedTemporaryFile(mode="w+t", suffix=".qlr")
+ tf.write(
+ ""
+ ""
+ ""
+ )
self.assertIsNone(get_url_datasource(tf))
def test_get_url_datasource_no_url_should_return_none(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".qlr")
- tf.write(""
- "empty"
- ""
- )
+ tf = NamedTemporaryFile(mode="w+t", suffix=".qlr")
+ tf.write(
+ ""
+ "empty"
+ ""
+ )
self.assertIsNone(get_url_datasource(tf))
@@ -86,9 +86,10 @@ def test_get_provider_should_succeed(self):
self.assertEqual(get_provider(f), "wms")
def test_get_provider_should_return_none(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".qlr")
- tf.write(""
- ""
- ""
- )
+ tf = NamedTemporaryFile(mode="w+t", suffix=".qlr")
+ tf.write(
+ ""
+ ""
+ ""
+ )
self.assertIsNone(get_provider(tf))
diff --git a/qgis-app/layerdefinitions/tests/test_forms.py b/qgis-app/layerdefinitions/tests/test_forms.py
index 7d336f90..931eb88e 100644
--- a/qgis-app/layerdefinitions/tests/test_forms.py
+++ b/qgis-app/layerdefinitions/tests/test_forms.py
@@ -1,13 +1,12 @@
-from django.test import TestCase
-
from base.forms.processing_forms import ResourceBaseReviewForm
+from django.test import TestCase
class TestFormResourceBaseReviewForm(TestCase):
def test_review_form_comment_includes_resource_name(self):
- form = ResourceBaseReviewForm(resource_name='test resource')
+ form = ResourceBaseReviewForm(resource_name="test resource")
self.assertIn(
'placeholder="Please provide clear feedback if you decided to not '
'approve this test resource." required id="id_comment"',
- form.as_table()
+ form.as_table(),
)
diff --git a/qgis-app/layerdefinitions/tests/test_views.py b/qgis-app/layerdefinitions/tests/test_views.py
index 170287de..f46119b2 100644
--- a/qgis-app/layerdefinitions/tests/test_views.py
+++ b/qgis-app/layerdefinitions/tests/test_views.py
@@ -3,39 +3,37 @@
import tempfile
import zipfile
+from django.contrib.auth.models import Group, User
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse
-from django.contrib.auth.models import User, Group
from layerdefinitions.models import LayerDefinition
-TESTFILES_DIR = os.path.join(os.path.dirname(__file__), 'testfiles')
+TESTFILES_DIR = os.path.join(os.path.dirname(__file__), "testfiles")
-class SetUpTest():
+class SetUpTest:
"""SetUp for all Test Class"""
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
- self.thumbnail = os.path.join(TESTFILES_DIR, 'thumbnail.png')
- self.thumbnail_content = open(self.thumbnail, 'rb')
- self.file = os.path.join(TESTFILES_DIR, 'my-vapour-pressure.qlr')
- self.file_content = open(self.file, 'rb')
+ self.thumbnail = os.path.join(TESTFILES_DIR, "thumbnail.png")
+ self.thumbnail_content = open(self.thumbnail, "rb")
+ self.file = os.path.join(TESTFILES_DIR, "my-vapour-pressure.qlr")
+ self.file_content = open(self.file, "rb")
self.creator = User.objects.create(
- username='creator', email='creator@email.com'
+ username="creator", email="creator@email.com"
)
# set creator password to password
- self.creator.set_password('password')
+ self.creator.set_password("password")
self.creator.save()
- self.staff = User.objects.create(
- username='staff', email='staff@email.com'
- )
- self.staff.set_password('password')
+ self.staff = User.objects.create(username="staff", email="staff@email.com")
+ self.staff.set_password("password")
self.staff.save()
- self.group = Group.objects.create(name='Style Managers')
+ self.group = Group.objects.create(name="Style Managers")
self.group.user_set.add(self.staff)
def tearDown(self):
@@ -44,47 +42,42 @@ def tearDown(self):
class TestPageUserAnonymous(TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def test_url(self):
- url = reverse('layerdefinition_list')
+ url = reverse("layerdefinition_list")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'All Layer Definition Files')
- self.assertContains(response, 'No data.')
+ self.assertContains(response, "All Layer Definition Files")
+ self.assertContains(response, "No data.")
def test_upload(self):
- url = reverse('layerdefinition_create')
+ url = reverse("layerdefinition_create")
response = self.client.get(url)
- self.assertRedirects(
- response,
- '/accounts/login/?next=/layerdefinitions/add/'
- )
+ self.assertRedirects(response, "/accounts/login/?next=/layerdefinitions/add/")
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class TestUploadLayerDefinitionFile(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
super(TestUploadLayerDefinitionFile, self).setUp()
- login = self.client.login(username='creator', password='password')
+ login = self.client.login(username="creator", password="password")
self.assertTrue(login)
- url = reverse('layerdefinition_create')
+ url = reverse("layerdefinition_create")
self.uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
self.uploaded_file = SimpleUploadedFile(
- self.file_content.name,
- self.file_content.read()
+ self.file_content.name, self.file_content.read()
)
self.data = {
- 'name': 'Test QLR File',
- 'description': 'Test upload a QLR File',
- 'thumbnail_image': self.uploaded_thumbnail,
- 'file': self.uploaded_file,
- 'license': 'license'
+ "name": "Test QLR File",
+ "description": "Test upload a QLR File",
+ "thumbnail_image": self.uploaded_thumbnail,
+ "file": self.uploaded_file,
+ "license": "license",
}
self.response = self.client.post(url, self.data, follow=True)
@@ -94,92 +87,76 @@ def test_upload_file_succeed_send_notification(self):
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].subject,
- 'A new Layer Definition File has been created by creator.'
+ "A new Layer Definition File has been created by creator.",
)
def test_uploaded_file_create_model_instance(self):
qlr = LayerDefinition.objects.first()
- self.assertEqual(qlr.name, self.data['name'])
- self.assertEqual(qlr.description, self.data['description'])
+ self.assertEqual(qlr.name, self.data["name"])
+ self.assertEqual(qlr.description, self.data["description"])
self.assertEqual(qlr.approved, False)
- self.assertEqual(qlr.license, self.data['license'])
- self.assertEqual(qlr.provider, 'wms')
+ self.assertEqual(qlr.license, self.data["license"])
+ self.assertEqual(qlr.provider, "wms")
self.assertEqual(
- qlr.url_datasource,
- 'https://maps.kartoza.com/geoserver/kartoza/wms'
+ qlr.url_datasource, "https://maps.kartoza.com/geoserver/kartoza/wms"
)
@override_settings(MEDIA_ROOT="layerdefinitions/tests/testfiles/")
class TestReviewLayerDefinition(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
super(TestReviewLayerDefinition, self).setUp()
self.qlr_object = LayerDefinition.objects.create(
creator=self.creator,
- name='Test QLR File',
- description='A QLR file for testing purpose',
+ name="Test QLR File",
+ description="A QLR file for testing purpose",
thumbnail_image=self.thumbnail,
- file=self.file
+ file=self.file,
)
def test_approval_layerdefinition_flow_approved(self):
- login = self.client.login(username='staff', password='password')
+ login = self.client.login(username="staff", password="password")
self.assertTrue(login)
- url = reverse(
- 'layerdefinition_review',
- kwargs={'pk': self.qlr_object.id}
+ url = reverse("layerdefinition_review", kwargs={"pk": self.qlr_object.id})
+ response = self.client.post(
+ url,
+ {"approval": "approve", "comment": "This should be in qlr approval page."},
)
- response = self.client.post(url, {
- 'approval': 'approve',
- 'comment': 'This should be in qlr approval page.'
- })
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- url = reverse(
- 'layerdefinition_detail',
- kwargs={'pk': self.qlr_object.id}
- )
+ url = reverse("layerdefinition_detail", kwargs={"pk": self.qlr_object.id})
self.assertRedirects(response, url)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertNotContains(
- response,
- 'This should be in qlr approval page.'
- )
- self.assertContains(response, 'Approved Date')
+ self.assertNotContains(response, "This should be in qlr approval page.")
+ self.assertContains(response, "Approved Date")
self.client.logout()
def test_approval_layerdefinition_flow_rejected(self):
login = self.client.login(username="staff", password="password")
self.assertTrue(login)
- url = reverse(
- 'layerdefinition_review',
- kwargs={'pk': self.qlr_object.id}
+ url = reverse("layerdefinition_review", kwargs={"pk": self.qlr_object.id})
+ response = self.client.post(
+ url,
+ {
+ "approval": "reject",
+ "comment": "This should be in requiring update page.",
+ },
)
- response = self.client.post(url, {
- 'approval': 'reject',
- 'comment': 'This should be in requiring update page.'
- })
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- url = reverse(
- 'layerdefinition_detail',
- kwargs={'pk': self.qlr_object.id}
- )
+ url = reverse("layerdefinition_detail", kwargs={"pk": self.qlr_object.id})
self.assertRedirects(response, url)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertContains(
- response,
- 'This should be in requiring update page.'
- )
- self.assertContains(response, 'Reviewed by Staff now')
+ self.assertContains(response, "This should be in requiring update page.")
+ self.assertContains(response, "Reviewed by Staff now")
self.client.logout()
# creator should find the rejected styles in requiring update page
self.client.login(username="creator", password="password")
- url = reverse('layerdefinition_require_action')
+ url = reverse("layerdefinition_require_action")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "1 record found.")
@@ -188,26 +165,24 @@ def test_approval_layerdefinition_flow_rejected(self):
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class TestDownloadLayerDefinition(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
super(TestDownloadLayerDefinition, self).setUp()
- self.client.login(username='creator', password='password')
- url = reverse('layerdefinition_create')
+ self.client.login(username="creator", password="password")
+ url = reverse("layerdefinition_create")
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_file = SimpleUploadedFile(
- self.file_content.name,
- self.file_content.read()
+ self.file_content.name, self.file_content.read()
)
data = {
- 'name': 'Test QLR File',
- 'description': 'Test upload a QLR File',
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_file,
- 'license': 'license'
+ "name": "Test QLR File",
+ "description": "Test upload a QLR File",
+ "thumbnail_image": uploaded_thumbnail,
+ "file": uploaded_file,
+ "license": "license",
}
self.client.post(url, data, follow=True)
@@ -215,19 +190,19 @@ def test_download_should_return_zipfile_with_custom_license(self):
qlr = LayerDefinition.objects.last()
qlr.approved = True
qlr.save()
- url = reverse('layerdefinition_download', kwargs={'pk': qlr.id})
+ url = reverse("layerdefinition_download", kwargs={"pk": qlr.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEquals(
- response.get('Content-Disposition'),
- 'attachment; filename=test-qlr-file.zip'
+ response.get("Content-Disposition"),
+ "attachment; filename=test-qlr-file.zip",
)
expected_filename_list = [
- 'Test QLR File/my-vapour-pressure.qlr',
- 'Test QLR File/license.txt'
+ "Test QLR File/my-vapour-pressure.qlr",
+ "Test QLR File/license.txt",
]
with io.BytesIO(response.content) as file:
- zip_file = zipfile.ZipFile(file, 'r')
+ zip_file = zipfile.ZipFile(file, "r")
self.assertIsNone(zip_file.testzip())
for f in expected_filename_list:
self.assertIn(f, zip_file.namelist())
diff --git a/qgis-app/layerdefinitions/urls.py b/qgis-app/layerdefinitions/urls.py
index 7204ee15..16a211cb 100644
--- a/qgis-app/layerdefinitions/urls.py
+++ b/qgis-app/layerdefinitions/urls.py
@@ -1,39 +1,56 @@
from django.urls import path
-
-from layerdefinitions.views import (LayerDefinitionCreateView,
- LayerDefinitionDetailView,
- LayerDefinitionUpdateView,
- LayerDefinitionListView,
- LayerDefinitionDeleteView,
- LayerDefinitionUnapprovedListView,
- LayerDefinitionRequireActionListView,
- LayerDefinitionReviewView,
- LayerDefinitionDownloadView,
- layerdefinition_nav_content)
-
+from layerdefinitions.views import (
+ LayerDefinitionCreateView,
+ LayerDefinitionDeleteView,
+ LayerDefinitionDetailView,
+ LayerDefinitionDownloadView,
+ LayerDefinitionListView,
+ LayerDefinitionRequireActionListView,
+ LayerDefinitionReviewView,
+ LayerDefinitionUnapprovedListView,
+ LayerDefinitionUpdateView,
+ layerdefinition_nav_content,
+)
urlpatterns = [
# GeoPackage
- path('', LayerDefinitionListView.as_view(), name='layerdefinition_list'),
- path('add/', LayerDefinitionCreateView.as_view(),
- name='layerdefinition_create'),
- path('/', LayerDefinitionDetailView.as_view(),
- name='layerdefinition_detail'),
- path('/update/', LayerDefinitionUpdateView.as_view(),
- name='layerdefinition_update'),
- path('/delete/', LayerDefinitionDeleteView.as_view(),
- name='layerdefinition_delete'),
- path('/review/', LayerDefinitionReviewView.as_view(),
- name='layerdefinition_review'),
- path('/download/', LayerDefinitionDownloadView.as_view(),
- name='layerdefinition_download'),
-
- path('unapproved/', LayerDefinitionUnapprovedListView.as_view(),
- name='layerdefinition_unapproved'),
- path('require_action/', LayerDefinitionRequireActionListView.as_view(),
- name='layerdefinition_require_action'),
-
+ path("", LayerDefinitionListView.as_view(), name="layerdefinition_list"),
+ path("add/", LayerDefinitionCreateView.as_view(), name="layerdefinition_create"),
+ path(
+ "/", LayerDefinitionDetailView.as_view(), name="layerdefinition_detail"
+ ),
+ path(
+ "/update/",
+ LayerDefinitionUpdateView.as_view(),
+ name="layerdefinition_update",
+ ),
+ path(
+ "/delete/",
+ LayerDefinitionDeleteView.as_view(),
+ name="layerdefinition_delete",
+ ),
+ path(
+ "/review/",
+ LayerDefinitionReviewView.as_view(),
+ name="layerdefinition_review",
+ ),
+ path(
+ "/download/",
+ LayerDefinitionDownloadView.as_view(),
+ name="layerdefinition_download",
+ ),
+ path(
+ "unapproved/",
+ LayerDefinitionUnapprovedListView.as_view(),
+ name="layerdefinition_unapproved",
+ ),
+ path(
+ "require_action/",
+ LayerDefinitionRequireActionListView.as_view(),
+ name="layerdefinition_require_action",
+ ),
# JSON
- path('sidebarnav/', layerdefinition_nav_content,
- name="layerdefinition_nav_content"),
+ path(
+ "sidebarnav/", layerdefinition_nav_content, name="layerdefinition_nav_content"
+ ),
]
diff --git a/qgis-app/layerdefinitions/views.py b/qgis-app/layerdefinitions/views.py
index dac10053..5309e543 100644
--- a/qgis-app/layerdefinitions/views.py
+++ b/qgis-app/layerdefinitions/views.py
@@ -1,27 +1,32 @@
-from base.views.processing_view import (ResourceBaseCreateView,
- ResourceBaseDetailView,
- ResourceBaseUpdateView,
- ResourceBaseListView,
- ResourceBaseUnapprovedListView,
- ResourceBaseRequireActionListView,
- ResourceBaseDeleteView,
- ResourceBaseReviewView,
- ResourceBaseDownload,
- resource_nav_content)
-from base.views.processing_view import _, resource_notify, messages
-from base.views.processing_view import HttpResponseRedirect, reverse_lazy
from base.views.processing_view import (
- get_object_or_404, check_resources_access, TemplateResponse,
- HttpResponse, slugify
+ HttpResponse,
+ HttpResponseRedirect,
+ ResourceBaseCreateView,
+ ResourceBaseDeleteView,
+ ResourceBaseDetailView,
+ ResourceBaseDownload,
+ ResourceBaseListView,
+ ResourceBaseRequireActionListView,
+ ResourceBaseReviewView,
+ ResourceBaseUnapprovedListView,
+ ResourceBaseUpdateView,
+ TemplateResponse,
+ _,
+ check_resources_access,
+ get_object_or_404,
+ messages,
+ resource_nav_content,
+ resource_notify,
+ reverse_lazy,
+ slugify,
)
-
-from layerdefinitions.file_handler import get_url_datasource, get_provider
-from layerdefinitions.forms import (UpdateForm, UploadForm)
-from layerdefinitions.models import LayerDefinition, Review
+from layerdefinitions.file_handler import get_provider, get_url_datasource
+from layerdefinitions.forms import UpdateForm, UploadForm
from layerdefinitions.license import zipped_with_license
+from layerdefinitions.models import LayerDefinition, Review
-class ResourceMixin():
+class ResourceMixin:
"""Mixin class for Geopackage."""
model = LayerDefinition
@@ -29,10 +34,10 @@ class ResourceMixin():
review_model = Review
# The resource_name will be displayed as the app name on web page
- resource_name = 'Layer Definition File'
+ resource_name = "Layer Definition File"
# The url name in urls.py should start start with this value
- resource_name_url_base = 'layerdefinition'
+ resource_name_url_base = "layerdefinition"
class LayerDefinitionCreateView(ResourceMixin, ResourceBaseCreateView):
@@ -49,19 +54,19 @@ def form_valid(self, form):
obj.save()
resource_notify(obj, resource_type=self.resource_name)
msg = _(self.success_message)
- messages.success(self.request, msg, 'success', fail_silently=True)
+ messages.success(self.request, msg, "success", fail_silently=True)
return super(ResourceBaseCreateView, self).form_valid(form)
class LayerDefinitionDetailView(ResourceMixin, ResourceBaseDetailView):
"""Detail View"""
- license_template = 'base/includes/layerdefinition/license.html'
- css = ('css/detail_page.css',)
+ license_template = "base/includes/layerdefinition/license.html"
+ css = ("css/detail_page.css",)
def get_context_data(self, **kwargs):
context = super(LayerDefinitionDetailView, self).get_context_data()
- context['is_qlr'] = True
+ context["is_qlr"] = True
return context
@@ -80,23 +85,22 @@ def form_valid(self, form):
obj.save()
resource_notify(obj, created=False, resource_type=self.resource_name)
msg = _("The %s has been successfully updated." % self.resource_name)
- messages.success(self.request, msg, 'success', fail_silently=True)
- url_name = '%s_detail' % self.resource_name_url_base
- return HttpResponseRedirect(
- reverse_lazy(url_name, kwargs={'pk': obj.id}))
+ messages.success(self.request, msg, "success", fail_silently=True)
+ url_name = "%s_detail" % self.resource_name_url_base
+ return HttpResponseRedirect(reverse_lazy(url_name, kwargs={"pk": obj.id}))
class LayerDefinitionListView(ResourceMixin, ResourceBaseListView):
"""Approved Layer Definition File (.qlr) ListView"""
-class LayerDefinitionUnapprovedListView(ResourceMixin,
- ResourceBaseUnapprovedListView):
+class LayerDefinitionUnapprovedListView(ResourceMixin, ResourceBaseUnapprovedListView):
"""Unapproved Layer Definition File (.qlr) ListView"""
-class LayerDefinitionRequireActionListView(ResourceMixin,
- ResourceBaseRequireActionListView):
+class LayerDefinitionRequireActionListView(
+ ResourceMixin, ResourceBaseRequireActionListView
+):
"""Layer Definition File (.qlr) Requires Action"""
@@ -112,13 +116,14 @@ class LayerDefinitionDownloadView(ResourceMixin, ResourceBaseDownload):
"""Download a Layer Definition File (.qlr)."""
def get(self, request, *args, **kwargs):
- object = get_object_or_404(self.model, pk=self.kwargs['pk'])
+ object = get_object_or_404(self.model, pk=self.kwargs["pk"])
if not object.approved:
if not check_resources_access(self.request.user, object):
context = super(ResourceBaseDownload, self).get_context_data()
- context['object_name'] = object.name
- context['context'] = ('Download failed. This %s is '
- 'not approved' % self.resource_name)
+ context["object_name"] = object.name
+ context["context"] = (
+ "Download failed. This %s is " "not approved" % self.resource_name
+ )
return TemplateResponse(request, self.template_name, context)
else:
object.increase_download_counter()
@@ -128,12 +133,13 @@ def get(self, request, *args, **kwargs):
zipfile = zipped_with_license(
file=object.file.file.name,
zip_subdir=object.name,
- custom_license=object.license
+ custom_license=object.license,
)
response = HttpResponse(
- zipfile.getvalue(), content_type="application/x-zip-compressed")
- response['Content-Disposition'] = 'attachment; filename=%s.zip' % (
+ zipfile.getvalue(), content_type="application/x-zip-compressed"
+ )
+ response["Content-Disposition"] = "attachment; filename=%s.zip" % (
slugify(object.name, allow_unicode=True)
)
return response
diff --git a/qgis-app/lib/templatetags/avatar_exists.py b/qgis-app/lib/templatetags/avatar_exists.py
index 59fc8fd7..db990a2a 100644
--- a/qgis-app/lib/templatetags/avatar_exists.py
+++ b/qgis-app/lib/templatetags/avatar_exists.py
@@ -1,12 +1,13 @@
-from django.template import Library
-from django.core.files.storage import default_storage
from django.conf import settings
+from django.core.files.storage import default_storage
+from django.template import Library
register = Library()
+
@register.filter
-def avatar_exists( value ):
+def avatar_exists(value):
"""
Test if an avatar exists
"""
- default_storage.exists('%simg/faces/%s.png' % (settings.MEDIA_ROOT, value))
+ default_storage.exists("%simg/faces/%s.png" % (settings.MEDIA_ROOT, value))
diff --git a/qgis-app/lib/templatetags/sort_anchor.py b/qgis-app/lib/templatetags/sort_anchor.py
index 19443f8a..c4342431 100644
--- a/qgis-app/lib/templatetags/sort_anchor.py
+++ b/qgis-app/lib/templatetags/sort_anchor.py
@@ -3,25 +3,26 @@
from django.conf import settings
from django.template import Library
-DEFAULT_SORT_UP = getattr(settings, 'DEFAULT_SORT_UP' , '↑')
-DEFAULT_SORT_DOWN = getattr(settings, 'DEFAULT_SORT_DOWN' , '↓')
+DEFAULT_SORT_UP = getattr(settings, "DEFAULT_SORT_UP", "↑")
+DEFAULT_SORT_DOWN = getattr(settings, "DEFAULT_SORT_DOWN", "↓")
register = template.Library()
sort_directions = {
- 'asc': {'icon':DEFAULT_SORT_UP, 'inverse': 'desc'},
- 'desc': {'icon':DEFAULT_SORT_DOWN, 'inverse': 'asc'},
- '': {'icon':DEFAULT_SORT_DOWN, 'inverse': 'asc'},
+ "asc": {"icon": DEFAULT_SORT_UP, "inverse": "desc"},
+ "desc": {"icon": DEFAULT_SORT_DOWN, "inverse": "asc"},
+ "": {"icon": DEFAULT_SORT_DOWN, "inverse": "asc"},
}
+
@register.tag
def anchor(parser, token):
"""
Parses a tag that's supposed to be in this format: {% anchor field title %}
"""
- bits = [b.strip('"\'') for b in token.split_contents()]
+ bits = [b.strip("\"'") for b in token.split_contents()]
if len(bits) < 2:
raise template.TemplateSyntaxError("anchor tag takes at least 1 argument")
try:
@@ -41,49 +42,50 @@ class SortAnchorNode(template.Node):
{% anchor name Name %} generates
Name
"""
+
def __init__(self, field, title):
self.field = field
self.title = title
def render(self, context):
- request = context['request']
+ request = context["request"]
getvars = request.GET.copy()
- if 'sort' in getvars:
- sortby = getvars['sort']
- del getvars['sort']
+ if "sort" in getvars:
+ sortby = getvars["sort"]
+ del getvars["sort"]
else:
- sortby = ''
+ sortby = ""
- if sortby.startswith('-'):
- sortdir='desc'
+ if sortby.startswith("-"):
+ sortdir = "desc"
sortby = sortby[1:]
else:
- sortdir='asc'
+ sortdir = "asc"
# Invert
if sortby == self.field:
- icon = sort_directions[sortdir]['icon']
- sortdir = sort_directions[sortdir]['inverse']
+ icon = sort_directions[sortdir]["icon"]
+ sortdir = sort_directions[sortdir]["inverse"]
else:
- icon = ''
+ icon = ""
if len(getvars.keys()) > 0:
urlappend = "&%s" % getvars.urlencode()
else:
- urlappend = ''
+ urlappend = ""
if icon:
title = "%s %s" % (self.title, icon)
else:
title = self.title
- valid_fields = getattr(request, 'valid_fields', [])
+ valid_fields = getattr(request, "valid_fields", [])
valid_fields.append(self.field)
- setattr(request, 'valid_fields', valid_fields)
+ setattr(request, "valid_fields", valid_fields)
# Inverse?
- if sortby == self.field and sortdir == 'desc':
- field = '-%s' % self.field
+ if sortby == self.field and sortdir == "desc":
+ field = "-%s" % self.field
else:
field = self.field
- url = '%s?sort=%s%s' % (request.path, field, urlappend)
+ url = "%s?sort=%s%s" % (request.path, field, urlappend)
return '%s' % (url, self.title, title)
diff --git a/qgis-app/middleware.py b/qgis-app/middleware.py
index 0bb8be3f..e523ac6e 100644
--- a/qgis-app/middleware.py
+++ b/qgis-app/middleware.py
@@ -1,4 +1,4 @@
-#-*- coding:utf-8 -*-
+# -*- coding:utf-8 -*-
"""
QGIS-DJANGO - MIDDLEWARE
diff --git a/qgis-app/models/admin.py b/qgis-app/models/admin.py
index a38f23ec..932fbab1 100644
--- a/qgis-app/models/admin.py
+++ b/qgis-app/models/admin.py
@@ -4,16 +4,31 @@
class ModelInline(admin.TabularInline):
model = Review
- list_display = ('review_date', 'comment', 'reviewer')
+ list_display = ("review_date", "comment", "reviewer")
@admin.register(Model)
class ModelAdmin(admin.ModelAdmin):
- inlines = [ModelInline, ]
- list_display = ('name', 'description', 'creator', 'upload_date',)
- search_fields = ('name', 'description',)
+ inlines = [
+ ModelInline,
+ ]
+ list_display = (
+ "name",
+ "description",
+ "creator",
+ "upload_date",
+ )
+ search_fields = (
+ "name",
+ "description",
+ )
@admin.register(Review)
class ModelReviewAdmin(admin.ModelAdmin):
- list_display = ('resource', 'reviewer', 'comment', 'review_date',)
+ list_display = (
+ "resource",
+ "reviewer",
+ "comment",
+ "review_date",
+ )
diff --git a/qgis-app/models/apps.py b/qgis-app/models/apps.py
index a00210ee..3b7bceba 100644
--- a/qgis-app/models/apps.py
+++ b/qgis-app/models/apps.py
@@ -2,4 +2,4 @@
class ModelsConfig(AppConfig):
- name = 'models'
+ name = "models"
diff --git a/qgis-app/models/forms.py b/qgis-app/models/forms.py
index d16e1c51..6e1e22b4 100644
--- a/qgis-app/models/forms.py
+++ b/qgis-app/models/forms.py
@@ -1,14 +1,17 @@
+from base.forms.processing_forms import ResourceBaseCleanFileForm
from django import forms
-
from models.models import Model
-from base.forms.processing_forms import ResourceBaseCleanFileForm
-
class ResourceFormMixin(forms.ModelForm):
class Meta:
model = Model
- fields = ['file', 'thumbnail_image', 'name', 'description', ]
+ fields = [
+ "file",
+ "thumbnail_image",
+ "name",
+ "description",
+ ]
class UploadForm(ResourceBaseCleanFileForm, ResourceFormMixin):
diff --git a/qgis-app/models/migrations/0001_initial.py b/qgis-app/models/migrations/0001_initial.py
index 4ad50b6f..5a1590d2 100644
--- a/qgis-app/models/migrations/0001_initial.py
+++ b/qgis-app/models/migrations/0001_initial.py
@@ -1,9 +1,9 @@
# Generated by Django 2.2.13 on 2020-12-16 21:46
-from django.conf import settings
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -16,36 +16,164 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
- name='Model',
+ name="Model",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('upload_date', models.DateTimeField(auto_now_add=True, help_text='The upload date. Automatically added on file upload.', verbose_name='Uploaded on')),
- ('modified_date', models.DateTimeField(editable=False, help_text='The upload date. Automatically added on file upload.', verbose_name='Modified on')),
- ('name', models.CharField(help_text='A unique name for this resource', max_length=256, unique=True, verbose_name='Name')),
- ('description', models.TextField(help_text='A description of this resource.', max_length=5000, verbose_name='Description')),
- ('download_count', models.IntegerField(default=0, editable=False, help_text='The number of times this resource has been downloaded. This is updated automatically.', verbose_name='Downloads')),
- ('approved', models.BooleanField(db_index=True, default=False, help_text='Set to True if you wish to approve this resource.', verbose_name='Approved')),
- ('require_action', models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update the resource.', verbose_name='Requires Action')),
- ('thumbnail_image', models.ImageField(help_text='Please upload an image that demonstrate this Model', upload_to='models/%Y', verbose_name='Thumbnail')),
- ('model_file', models.FileField(help_text='A Model file. The filesize must less than 1MB ', upload_to='models/%Y', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['model3', 'zip'])], verbose_name='Model file')),
- ('creator', models.ForeignKey(help_text='The user who uploaded this resource.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "upload_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Uploaded on",
+ ),
+ ),
+ (
+ "modified_date",
+ models.DateTimeField(
+ editable=False,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Modified on",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ help_text="A unique name for this resource",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
+ ),
+ (
+ "description",
+ models.TextField(
+ help_text="A description of this resource.",
+ max_length=5000,
+ verbose_name="Description",
+ ),
+ ),
+ (
+ "download_count",
+ models.IntegerField(
+ default=0,
+ editable=False,
+ help_text="The number of times this resource has been downloaded. This is updated automatically.",
+ verbose_name="Downloads",
+ ),
+ ),
+ (
+ "approved",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you wish to approve this resource.",
+ verbose_name="Approved",
+ ),
+ ),
+ (
+ "require_action",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update the resource.",
+ verbose_name="Requires Action",
+ ),
+ ),
+ (
+ "thumbnail_image",
+ models.ImageField(
+ help_text="Please upload an image that demonstrate this Model",
+ upload_to="models/%Y",
+ verbose_name="Thumbnail",
+ ),
+ ),
+ (
+ "model_file",
+ models.FileField(
+ help_text="A Model file. The filesize must less than 1MB ",
+ upload_to="models/%Y",
+ validators=[
+ django.core.validators.FileExtensionValidator(
+ allowed_extensions=["model3", "zip"]
+ )
+ ],
+ verbose_name="Model file",
+ ),
+ ),
+ (
+ "creator",
+ models.ForeignKey(
+ help_text="The user who uploaded this resource.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
+ ),
],
options={
- 'abstract': False,
+ "abstract": False,
},
),
migrations.CreateModel(
- name='ModelReview',
+ name="ModelReview",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('review_date', models.DateTimeField(auto_now_add=True, help_text='The review date. Automatically added on review resource.', verbose_name='Reviewed on')),
- ('comment', models.TextField(blank=True, help_text='A review comment. Please write your review.', max_length=1000, null=True, verbose_name='Comment')),
- ('model', models.ForeignKey(help_text='The reviewed Model', on_delete=django.db.models.deletion.CASCADE, to='models.Model', verbose_name='Model')),
- ('reviewer', models.ForeignKey(help_text='The user who reviewed this GeoPackage.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "review_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The review date. Automatically added on review resource.",
+ verbose_name="Reviewed on",
+ ),
+ ),
+ (
+ "comment",
+ models.TextField(
+ blank=True,
+ help_text="A review comment. Please write your review.",
+ max_length=1000,
+ null=True,
+ verbose_name="Comment",
+ ),
+ ),
+ (
+ "model",
+ models.ForeignKey(
+ help_text="The reviewed Model",
+ on_delete=django.db.models.deletion.CASCADE,
+ to="models.Model",
+ verbose_name="Model",
+ ),
+ ),
+ (
+ "reviewer",
+ models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
+ ),
],
options={
- 'ordering': ['review_date'],
- 'abstract': False,
+ "ordering": ["review_date"],
+ "abstract": False,
},
),
]
diff --git a/qgis-app/models/migrations/0002_rename_Review_model_and_file_field.py b/qgis-app/models/migrations/0002_rename_Review_model_and_file_field.py
index e9ba2fd0..3d4baff8 100644
--- a/qgis-app/models/migrations/0002_rename_Review_model_and_file_field.py
+++ b/qgis-app/models/migrations/0002_rename_Review_model_and_file_field.py
@@ -1,41 +1,38 @@
# Custom migration, rename ModelReview to Review
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('models', '0001_initial'),
+ ("models", "0001_initial"),
]
operations = [
migrations.RenameField(
- model_name='model',
- old_name='model_file',
- new_name='file',
+ model_name="model",
+ old_name="model_file",
+ new_name="file",
),
-
migrations.RenameField(
- model_name='modelreview',
- old_name='model',
- new_name='resource',
+ model_name="modelreview",
+ old_name="model",
+ new_name="resource",
),
-
- migrations.RenameModel(
- old_name='ModelReview',
- new_name='Review'
- ),
-
+ migrations.RenameModel(old_name="ModelReview", new_name="Review"),
migrations.AlterField(
- model_name='review',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this GeoPackage.',
- on_delete=django.db.models.deletion.CASCADE, related_name='models_review_related',
- to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="review",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="models_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
-
]
diff --git a/qgis-app/models/migrations/0003_auto_20210203_2142.py b/qgis-app/models/migrations/0003_auto_20210203_2142.py
index 9ca7709e..d1053552 100644
--- a/qgis-app/models/migrations/0003_auto_20210203_2142.py
+++ b/qgis-app/models/migrations/0003_auto_20210203_2142.py
@@ -1,20 +1,26 @@
# Generated by Django 2.2.13 on 2021-02-03 21:42
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
- ('models', '0002_rename_Review_model_and_file_field'),
+ ("models", "0002_rename_Review_model_and_file_field"),
]
operations = [
migrations.AlterField(
- model_name='review',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='models_review_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="review",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this %(app_label)s.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="models_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/models/migrations/0004_add_uuid_field.py b/qgis-app/models/migrations/0004_add_uuid_field.py
index 39ff1155..cbd8ca58 100644
--- a/qgis-app/models/migrations/0004_add_uuid_field.py
+++ b/qgis-app/models/migrations/0004_add_uuid_field.py
@@ -1,19 +1,20 @@
# Generated by Django 2.2.13 on 2021-02-10 19:39
-from django.db import migrations, models
import uuid
+from django.db import migrations, models
+
class Migration(migrations.Migration):
dependencies = [
- ('models', '0003_auto_20210203_2142'),
+ ("models", "0003_auto_20210203_2142"),
]
operations = [
migrations.AddField(
- model_name='model',
- name='uuid',
+ model_name="model",
+ name="uuid",
field=models.UUIDField(default=uuid.uuid4, null=True),
),
]
diff --git a/qgis-app/models/migrations/0005_populate_uuid_value.py b/qgis-app/models/migrations/0005_populate_uuid_value.py
index 480b0ee4..d8f95591 100644
--- a/qgis-app/models/migrations/0005_populate_uuid_value.py
+++ b/qgis-app/models/migrations/0005_populate_uuid_value.py
@@ -1,20 +1,21 @@
# Generated by Django 2.2.13 on 2021-02-10 19:39
-from django.db import migrations
import uuid
+from django.db import migrations
+
def gen_uuid(apps, schema_editor):
- Model = apps.get_model('models', 'Model')
+ Model = apps.get_model("models", "Model")
for row in Model.objects.all():
row.uuid = uuid.uuid4()
- row.save(update_fields=['uuid'])
+ row.save(update_fields=["uuid"])
class Migration(migrations.Migration):
dependencies = [
- ('models', '0004_add_uuid_field'),
+ ("models", "0004_add_uuid_field"),
]
operations = [
diff --git a/qgis-app/models/migrations/0006_remove_uuid_null.py b/qgis-app/models/migrations/0006_remove_uuid_null.py
index 46f222e0..918086c3 100644
--- a/qgis-app/models/migrations/0006_remove_uuid_null.py
+++ b/qgis-app/models/migrations/0006_remove_uuid_null.py
@@ -1,19 +1,20 @@
# Generated by Django 2.2.13 on 2021-02-10 19:39
-from django.db import migrations, models
import uuid
+from django.db import migrations, models
+
class Migration(migrations.Migration):
dependencies = [
- ('models', '0005_populate_uuid_value'),
+ ("models", "0005_populate_uuid_value"),
]
operations = [
migrations.AlterField(
- model_name='model',
- name='uuid',
+ model_name="model",
+ name="uuid",
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
diff --git a/qgis-app/models/models.py b/qgis-app/models/models.py
index d6b80a39..1786bd9e 100644
--- a/qgis-app/models/models.py
+++ b/qgis-app/models/models.py
@@ -1,15 +1,13 @@
import os
+from base.models.processing_models import Resource, ResourceReview
from django.conf import settings
from django.core.validators import FileExtensionValidator
from django.db import models
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
-from base.models.processing_models import Resource, ResourceReview
-
-MODELS_STORAGE_PATH = getattr(settings,
- 'MODELS_STORAGE_PATH', 'models/%Y')
+MODELS_STORAGE_PATH = getattr(settings, "MODELS_STORAGE_PATH", "models/%Y")
class Model(Resource):
@@ -20,27 +18,28 @@ class Model(Resource):
# thumbnail
thumbnail_image = models.ImageField(
- _('Thumbnail'),
- help_text=_('Please upload an image that demonstrate this Model'),
+ _("Thumbnail"),
+ help_text=_("Please upload an image that demonstrate this Model"),
blank=False,
null=False,
- upload_to=MODELS_STORAGE_PATH)
+ upload_to=MODELS_STORAGE_PATH,
+ )
# file
file = models.FileField(
- _('Model file'),
- help_text=_('A Model file. The filesize must less than 1MB '),
+ _("Model file"),
+ help_text=_("A Model file. The filesize must less than 1MB "),
upload_to=MODELS_STORAGE_PATH,
- validators=[FileExtensionValidator(
- allowed_extensions=['model3', 'zip'])],
- null=False)
+ validators=[FileExtensionValidator(allowed_extensions=["model3", "zip"])],
+ null=False,
+ )
def extension(self):
name, extension = os.path.splitext(self.file.name)
return extension
def get_absolute_url(self):
- return reverse('model_detail', args=(self.id,))
+ return reverse("model_detail", args=(self.id,))
class Review(ResourceReview):
@@ -49,9 +48,11 @@ class Review(ResourceReview):
"""
# Model resource
- resource = models.ForeignKey(Model,
- verbose_name=_('Model'),
- help_text=_('The reviewed Model'),
- blank=False,
- null=False,
- on_delete=models.CASCADE)
+ resource = models.ForeignKey(
+ Model,
+ verbose_name=_("Model"),
+ help_text=_("The reviewed Model"),
+ blank=False,
+ null=False,
+ on_delete=models.CASCADE,
+ )
diff --git a/qgis-app/models/templatetags/resources_custom_tags.py b/qgis-app/models/templatetags/resources_custom_tags.py
index 59b163a5..e7475c07 100644
--- a/qgis-app/models/templatetags/resources_custom_tags.py
+++ b/qgis-app/models/templatetags/resources_custom_tags.py
@@ -1,7 +1,6 @@
import os.path
import markdown
-
from django import template
from django.conf import settings
from django.forms import CheckboxInput
@@ -29,33 +28,33 @@ def anchor_sort_arrow(name, order_by, current_order, current_query):
class_arrow = ""
if not current_query:
- current_query = ''
+ current_query = ""
if not desc_current_order:
result = (
'%s '
''
''
- '' % (
- name, order_by, current_query, class_arrow,
- order_by, current_query))
+ ''
+ % (name, order_by, current_query, class_arrow, order_by, current_query)
+ )
else:
result = (
'%s '
''
''
- '' % (
- name, order_by, current_query,
- order_by, current_query, class_arrow))
+ ''
+ % (name, order_by, current_query, order_by, current_query, class_arrow)
+ )
return mark_safe(result)
-@register.filter(name='is_checkbox')
+@register.filter(name="is_checkbox")
def is_checkbox(field):
return isinstance(field.field.widget, CheckboxInput)
-@register.filter(name='md_to_html')
+@register.filter(name="md_to_html")
def md_to_html(md_string):
md = markdown.Markdown()
html = md.convert(md_string)
@@ -66,11 +65,11 @@ def md_to_html(md_string):
@register.simple_tag(takes_context=True)
def version_tag(context):
"""Reads current project release from the .version file."""
- version_file = os.path.join(settings.SITE_ROOT, '.version')
+ version_file = os.path.join(settings.SITE_ROOT, ".version")
try:
- with open(version_file, 'r') as file:
+ with open(version_file, "r") as file:
version = file.read()
- context['version'] = version
+ context["version"] = version
except IOError:
- context['version'] = 'Unknown'
- return context['version']
+ context["version"] = "Unknown"
+ return context["version"]
diff --git a/qgis-app/models/tests/test_views.py b/qgis-app/models/tests/test_views.py
index d25422c5..81dfc7c5 100644
--- a/qgis-app/models/tests/test_views.py
+++ b/qgis-app/models/tests/test_views.py
@@ -1,37 +1,34 @@
import os
import tempfile
+from base.views.processing_view import resource_notify, resource_update_notify
+from django.contrib.auth.models import Group, User
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse
-from django.contrib.auth.models import User, Group
-from models.models import Model, Review
-
-from base.views.processing_view import resource_notify, resource_update_notify
from models.forms import UploadForm
+from models.models import Model, Review
MODEL_DIR = os.path.join(os.path.dirname(__file__), "modelfiles")
-class SetUpTest():
+class SetUpTest:
"""
SetUp for all Test Class
"""
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
self.thumbnail = os.path.join(MODEL_DIR, "thumbnail.png")
- self.thumbnail_content = open(self.thumbnail, 'rb')
- self.file = os.path.join(MODEL_DIR,
- "example.model3")
- self.file_content = open(self.file, 'rb')
- self.modelzip_file = os.path.join(MODEL_DIR,
- "example.zip")
- self.modelzip_file_content = open(self.modelzip_file, 'rb')
+ self.thumbnail_content = open(self.thumbnail, "rb")
+ self.file = os.path.join(MODEL_DIR, "example.model3")
+ self.file_content = open(self.file, "rb")
+ self.modelzip_file = os.path.join(MODEL_DIR, "example.zip")
+ self.modelzip_file_content = open(self.modelzip_file, "rb")
self.model_oversize = os.path.join(MODEL_DIR, "dummy_oversize.model3")
- self.model_oversize_content = open(self.model_oversize, 'rb')
+ self.model_oversize_content = open(self.model_oversize, "rb")
self.creator = User.objects.create(
username="creator", email="creator@email.com"
@@ -39,9 +36,7 @@ def setUp(self):
# set creator password to password
self.creator.set_password("password")
self.creator.save()
- self.staff = User.objects.create(
- username="staff", email="staff@email.com"
- )
+ self.staff = User.objects.create(username="staff", email="staff@email.com")
self.staff.set_password("password")
self.staff.save()
self.group = Group.objects.create(name="Style Managers")
@@ -56,74 +51,57 @@ def tearDown(self):
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class TestFormValidation(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def test_form_with_valid_data(self):
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_model = SimpleUploadedFile(
- self.file_content.name,
- self.file_content.read()
+ self.file_content.name, self.file_content.read()
)
form = UploadForm(data={})
self.assertFalse(form.is_valid())
data = {
- "name": "flooded building extractor",
- "description": "Test upload with valid data"
- }
- file_data = {
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_model
+ "name": "flooded building extractor",
+ "description": "Test upload with valid data",
}
+ file_data = {"thumbnail_image": uploaded_thumbnail, "file": uploaded_model}
form = UploadForm(data, file_data)
self.assertTrue(form.is_valid())
def test_form_invalid_file_extension(self):
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_model = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
data = {
- "name": "flooded buildings extractor",
- "description": "Test upload invalid model file extension"
- }
- file_data = {
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_model
+ "name": "flooded buildings extractor",
+ "description": "Test upload invalid model file extension",
}
+ file_data = {"thumbnail_image": uploaded_thumbnail, "file": uploaded_model}
form = UploadForm(data, file_data)
self.assertFalse(form.is_valid())
- self.assertEqual(form.errors,
- {'file': ['The submitted file is empty.']})
+ self.assertEqual(form.errors, {"file": ["The submitted file is empty."]})
def test_form_invalid_filesize(self):
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_model = SimpleUploadedFile(
- self.model_oversize_content.name,
- self.model_oversize_content.read()
+ self.model_oversize_content.name, self.model_oversize_content.read()
)
data = {
- 'name': 'flooded buildings extractor',
- 'description': 'Test upload invalid model filesize'
- }
- file_data = {
- 'thumbnail_image': uploaded_thumbnail,
- 'file': uploaded_model
+ "name": "flooded buildings extractor",
+ "description": "Test upload invalid model filesize",
}
+ file_data = {"thumbnail_image": uploaded_thumbnail, "file": uploaded_model}
form = UploadForm(data, file_data)
self.assertFalse(form.is_valid())
self.assertEqual(
- form.errors,
- {'file': ['File is too big. Max size is 1.0 Megabytes']}
+ form.errors, {"file": ["File is too big. Max size is 1.0 Megabytes"]}
)
@@ -133,66 +111,61 @@ class TestEmailNotification(SetUpTest, TestCase):
Send the email to console
"""
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
- @override_settings(
- EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend')
+ @override_settings(EMAIL_BACKEND="django.core.mail.backends.console.EmailBackend")
def test_print_email_notification_in_console(self):
Model.objects.create(
creator=self.creator,
name="flooded buildings extractor",
description="A Model for testing purpose",
thumbnail_image=self.thumbnail,
- file=self.file
+ file=self.file,
)
model = Model.objects.first()
- resource_notify(model, resource_type='Model')
+ resource_notify(model, resource_type="Model")
Review.objects.create(
- reviewer=self.staff,
- resource=model,
- comment="Rejected for testing purpose")
+ reviewer=self.staff, resource=model, comment="Rejected for testing purpose"
+ )
model.require_action = True
model.save()
- resource_update_notify(model, self.creator, self.staff,
- resource_type='Model')
+ resource_update_notify(model, self.creator, self.staff, resource_type="Model")
Review.objects.create(
reviewer=self.staff,
resource=model,
- comment="Approved! This is for testing purpose")
+ comment="Approved! This is for testing purpose",
+ )
model.approved = True
model.save()
- resource_update_notify(model, self.creator, self.staff,
- resource_type='Model')
+ resource_update_notify(model, self.creator, self.staff, resource_type="Model")
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
class TestUploadModel(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def test_upload_acceptable_model3_size_file(self):
login = self.client.login(username="creator", password="password")
self.assertTrue(login)
url = reverse("model_create")
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_model = SimpleUploadedFile(
- self.file_content.name,
- self.file_content.read()
+ self.file_content.name, self.file_content.read()
)
data = {
"name": "flooded buildings extractor",
"description": "Test upload an acceptable model size",
"thumbnail_image": uploaded_thumbnail,
- "file": uploaded_model
+ "file": uploaded_model,
}
response = self.client.post(url, data, follow=True)
# should send email notify
self.assertEqual(len(mail.outbox), 1)
model = Model.objects.first()
self.assertEqual(model.name, "flooded buildings extractor")
- url = reverse("model_detail", kwargs={'pk': model.id})
+ url = reverse("model_detail", kwargs={"pk": model.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -201,25 +174,23 @@ def test_upload_acceptable_zip_size_file(self):
self.assertTrue(login)
url = reverse("model_create")
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_model = SimpleUploadedFile(
- self.modelzip_file_content.name,
- self.modelzip_file_content.read()
+ self.modelzip_file_content.name, self.modelzip_file_content.read()
)
data = {
"name": "flooded buildings extractor",
"description": "Test upload .zip model",
"thumbnail_image": uploaded_thumbnail,
- "file": uploaded_model
+ "file": uploaded_model,
}
response = self.client.post(url, data, follow=True)
# should send email notify
self.assertEqual(len(mail.outbox), 1)
model = Model.objects.first()
self.assertEqual(model.name, "flooded buildings extractor")
- url = reverse("model_detail", kwargs={'pk': model.id})
+ url = reverse("model_detail", kwargs={"pk": model.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -228,18 +199,16 @@ def test_upload_invalid_size_file(self):
self.assertTrue(login)
url = reverse("model_create")
uploaded_thumbnail = SimpleUploadedFile(
- self.thumbnail_content.name,
- self.thumbnail_content.read()
+ self.thumbnail_content.name, self.thumbnail_content.read()
)
uploaded_model = SimpleUploadedFile(
- self.model_oversize_content.name,
- self.model_oversize_content.read()
+ self.model_oversize_content.name, self.model_oversize_content.read()
)
data = {
"name": "flooded buildings extractor",
"description": "Test upload a model > 1Mb filesize",
"thumbnail_image": uploaded_thumbnail,
- "file": uploaded_model
+ "file": uploaded_model,
}
response = self.client.post(url, data, follow=True)
self.assertEqual(response.status_code, 200)
@@ -250,7 +219,7 @@ def test_upload_invalid_size_file(self):
@override_settings(MEDIA_ROOT="models/tests/modelfiles/")
class TestReviewModel(SetUpTest, TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self):
super(TestReviewModel, self).setUp()
@@ -259,48 +228,49 @@ def setUp(self):
name="flooded buildings extractor",
description="A Model for testing purpose",
thumbnail_image=self.thumbnail,
- file=self.file
+ file=self.file,
)
def test_approve_model(self):
login = self.client.login(username="staff", password="password")
self.assertTrue(login)
- url = reverse('model_review', kwargs={'pk': self.model_object.id})
- response = self.client.post(url, {
- 'approval': 'approve',
- 'comment': 'This should be in Approve page.'
- })
+ url = reverse("model_review", kwargs={"pk": self.model_object.id})
+ response = self.client.post(
+ url, {"approval": "approve", "comment": "This should be in Approve page."}
+ )
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- url = reverse('model_detail', kwargs={'pk': self.model_object.id})
+ url = reverse("model_detail", kwargs={"pk": self.model_object.id})
self.assertRedirects(response, url)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertNotContains(response, 'This should be in Approve page.')
- self.assertContains(response, 'Approved Date')
+ self.assertNotContains(response, "This should be in Approve page.")
+ self.assertContains(response, "Approved Date")
self.client.logout()
def test_reject_model(self):
login = self.client.login(username="staff", password="password")
self.assertTrue(login)
- url = reverse('model_review', kwargs={'pk': self.model_object.id})
- response = self.client.post(url, {
- 'approval': 'reject',
- 'comment': 'This should be in requiring update page.'
- })
+ url = reverse("model_review", kwargs={"pk": self.model_object.id})
+ response = self.client.post(
+ url,
+ {
+ "approval": "reject",
+ "comment": "This should be in requiring update page.",
+ },
+ )
# should send email notify
self.assertEqual(len(mail.outbox), 1)
- url = reverse('model_detail', kwargs={'pk': self.model_object.id})
+ url = reverse("model_detail", kwargs={"pk": self.model_object.id})
self.assertRedirects(response, url)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertContains(response,
- 'This should be in requiring update page.')
- self.assertContains(response, 'Reviewed by Staff now')
+ self.assertContains(response, "This should be in requiring update page.")
+ self.assertContains(response, "Reviewed by Staff now")
self.client.logout()
# creator should find the rejected styles in requiring update page
self.client.login(username="creator", password="password")
- url = reverse('model_require_action')
+ url = reverse("model_require_action")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "1 record found.")
diff --git a/qgis-app/models/urls.py b/qgis-app/models/urls.py
index 2235ffd1..f2945c7b 100644
--- a/qgis-app/models/urls.py
+++ b/qgis-app/models/urls.py
@@ -1,36 +1,32 @@
from django.urls import path
-
-from models.views import (ModelCreateView,
- ModelDetailView,
- ModelUpdateView,
- ModelListView,
- ModelDeleteView,
- ModelUnapprovedListView,
- ModelRequireActionListView,
- ModelReviewView,
- ModelDownloadView,
- model_nav_content,)
-
+from models.views import (
+ ModelCreateView,
+ ModelDeleteView,
+ ModelDetailView,
+ ModelDownloadView,
+ ModelListView,
+ ModelRequireActionListView,
+ ModelReviewView,
+ ModelUnapprovedListView,
+ ModelUpdateView,
+ model_nav_content,
+)
urlpatterns = [
# Model
- path('', ModelListView.as_view(), name='model_list'),
- path('add/', ModelCreateView.as_view(), name='model_create'),
- path('/', ModelDetailView.as_view(),
- name='model_detail'),
- path('/update/', ModelUpdateView.as_view(),
- name='model_update'),
- path('/delete/', ModelDeleteView.as_view(),
- name='model_delete'),
- path('/review/', ModelReviewView.as_view(), name='model_review'),
- path('/download/', ModelDownloadView.as_view(),
- name='model_download'),
-
- path('unapproved/', ModelUnapprovedListView.as_view(),
- name='model_unapproved'),
- path('require_action/', ModelRequireActionListView.as_view(),
- name='model_require_action'),
-
+ path("", ModelListView.as_view(), name="model_list"),
+ path("add/", ModelCreateView.as_view(), name="model_create"),
+ path("/", ModelDetailView.as_view(), name="model_detail"),
+ path("/update/", ModelUpdateView.as_view(), name="model_update"),
+ path("/delete/", ModelDeleteView.as_view(), name="model_delete"),
+ path("/review/", ModelReviewView.as_view(), name="model_review"),
+ path("/download/", ModelDownloadView.as_view(), name="model_download"),
+ path("unapproved/", ModelUnapprovedListView.as_view(), name="model_unapproved"),
+ path(
+ "require_action/",
+ ModelRequireActionListView.as_view(),
+ name="model_require_action",
+ ),
# JSON
- path('sidebarnav/', model_nav_content, name="model_nav_content"),
+ path("sidebarnav/", model_nav_content, name="model_nav_content"),
]
diff --git a/qgis-app/models/validator.py b/qgis-app/models/validator.py
index ecdcb162..e4d41829 100644
--- a/qgis-app/models/validator.py
+++ b/qgis-app/models/validator.py
@@ -4,7 +4,7 @@
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
-MODEL_MAX_SIZE = getattr(settings, 'MODEL_MAX_SIZE', 1000000) # 1MB
+MODEL_MAX_SIZE = getattr(settings, "MODEL_MAX_SIZE", 1000000) # 1MB
def model_validator(model_file) -> bool:
@@ -13,28 +13,32 @@ def model_validator(model_file) -> bool:
try:
if model_file.getbuffer().nbytes > MODEL_MAX_SIZE:
raise ValidationError(
- _("File is too big. Max size is %s Megabytes") % (
- MODEL_MAX_SIZE / 1000000))
+ _("File is too big. Max size is %s Megabytes")
+ % (MODEL_MAX_SIZE / 1000000)
+ )
except AttributeError:
try:
model_file.seek(0, os.SEEK_END)
print(model_file.seek(0, os.SEEK_END))
if model_file.seek(0, os.SEEK_END) > MODEL_MAX_SIZE:
raise ValidationError(
- _("File is too big. Max size is %s Megabytes") % (
- MODEL_MAX_SIZE / 1000000))
+ _("File is too big. Max size is %s Megabytes")
+ % (MODEL_MAX_SIZE / 1000000)
+ )
except AttributeError:
try:
if model_file.size > MODEL_MAX_SIZE:
raise ValidationError(
- _("File is too big. Max size is %s Megabytes") % (
- MODEL_MAX_SIZE / 1000000))
+ _("File is too big. Max size is %s Megabytes")
+ % (MODEL_MAX_SIZE / 1000000)
+ )
except AttributeError:
try:
if model_file.len > MODEL_MAX_SIZE:
raise ValidationError(
- _("File is too big. Max size is %s Megabytes") % (
- MODEL_MAX_SIZE / 1000000))
+ _("File is too big. Max size is %s Megabytes")
+ % (MODEL_MAX_SIZE / 1000000)
+ )
except Exception:
raise ValidationError(_("Can not read this file."))
return True
diff --git a/qgis-app/models/views.py b/qgis-app/models/views.py
index 16ccbc02..85acc06a 100644
--- a/qgis-app/models/views.py
+++ b/qgis-app/models/views.py
@@ -1,20 +1,20 @@
-from base.views.processing_view import (ResourceBaseCreateView,
- ResourceBaseDetailView,
- ResourceBaseUpdateView,
- ResourceBaseListView,
- ResourceBaseUnapprovedListView,
- ResourceBaseRequireActionListView,
- ResourceBaseDeleteView,
- ResourceBaseReviewView,
- ResourceBaseDownload,
- resource_nav_content)
-
-
+from base.views.processing_view import (
+ ResourceBaseCreateView,
+ ResourceBaseDeleteView,
+ ResourceBaseDetailView,
+ ResourceBaseDownload,
+ ResourceBaseListView,
+ ResourceBaseRequireActionListView,
+ ResourceBaseReviewView,
+ ResourceBaseUnapprovedListView,
+ ResourceBaseUpdateView,
+ resource_nav_content,
+)
from models.forms import UpdateForm, UploadForm
from models.models import Model, Review
-class ResourceMixin():
+class ResourceMixin:
"""Mixin class for Model."""
model = Model
@@ -22,10 +22,10 @@ class ResourceMixin():
review_model = Review
# The resource_name will be displayed as the app name on web page
- resource_name = 'Model'
+ resource_name = "Model"
# The url name in urls.py should start with this value
- resource_name_url_base = 'model'
+ resource_name_url_base = "model"
class ModelCreateView(ResourceMixin, ResourceBaseCreateView):
@@ -48,13 +48,11 @@ class ModelListView(ResourceMixin, ResourceBaseListView):
"""Approved Model ListView"""
-class ModelUnapprovedListView(ResourceMixin,
- ResourceBaseUnapprovedListView):
+class ModelUnapprovedListView(ResourceMixin, ResourceBaseUnapprovedListView):
"""Unapproved Model ListView"""
-class ModelRequireActionListView(ResourceMixin,
- ResourceBaseRequireActionListView):
+class ModelRequireActionListView(ResourceMixin, ResourceBaseRequireActionListView):
"""Model Requires Action"""
diff --git a/qgis-app/plugins/__init__.py b/qgis-app/plugins/__init__.py
index 273badb4..cf9dc02f 100644
--- a/qgis-app/plugins/__init__.py
+++ b/qgis-app/plugins/__init__.py
@@ -4,6 +4,14 @@
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
-default_app_config = 'plugins.apps.PluginsConfig'
-__all__ = ["celery_app", "models", "views", "admin", "urls", "api", "forms", "validator"]
-
+default_app_config = "plugins.apps.PluginsConfig"
+__all__ = [
+ "celery_app",
+ "models",
+ "views",
+ "admin",
+ "urls",
+ "api",
+ "forms",
+ "validator",
+]
diff --git a/qgis-app/plugins/admin.py b/qgis-app/plugins/admin.py
index 0ad61937..01649c9f 100644
--- a/qgis-app/plugins/admin.py
+++ b/qgis-app/plugins/admin.py
@@ -1,22 +1,37 @@
-from plugins.models import Plugin, PluginVersion #, PluginCrashReport
from django.contrib import admin
+from plugins.models import Plugin, PluginVersion # , PluginCrashReport
-class PluginAdmin (admin.ModelAdmin):
- list_filter = ('featured',)
- list_display = ('name', 'featured', 'created_by', 'created_on', 'downloads', 'stable', 'experimental')
- search_fields = ('name',)
+class PluginAdmin(admin.ModelAdmin):
+ list_filter = ("featured",)
+ list_display = (
+ "name",
+ "featured",
+ "created_by",
+ "created_on",
+ "downloads",
+ "stable",
+ "experimental",
+ )
+ search_fields = ("name",)
-class PluginVersionAdmin (admin.ModelAdmin):
- list_filter = ('experimental', 'approved', 'plugin')
- list_display = ('plugin', 'approved', 'version', 'experimental', 'created_on', 'downloads')
+class PluginVersionAdmin(admin.ModelAdmin):
+ list_filter = ("experimental", "approved", "plugin")
+ list_display = (
+ "plugin",
+ "approved",
+ "version",
+ "experimental",
+ "created_on",
+ "downloads",
+ )
-#class PluginCrashReportAdmin(admin.ModelAdmin):
- #pass
+# class PluginCrashReportAdmin(admin.ModelAdmin):
+# pass
admin.site.register(Plugin, PluginAdmin)
admin.site.register(PluginVersion, PluginVersionAdmin)
-#admin.site.register(PluginCrashReport, PluginCrashReportAdmin)
+# admin.site.register(PluginCrashReport, PluginCrashReportAdmin)
diff --git a/qgis-app/plugins/api.py b/qgis-app/plugins/api.py
index 7b32a39e..e2270fd4 100644
--- a/qgis-app/plugins/api.py
+++ b/qgis-app/plugins/api.py
@@ -2,37 +2,45 @@
XML-RPC webservices for the plugin web application
"""
-from rpc4django import rpcmethod
-from xmlrpc.server import Fault
-
-from plugins.validator import validator
-from plugins.models import *
-from plugins.views import plugin_notify
-
from io import BytesIO
-from taggit.models import Tag
+from xmlrpc.server import Fault
-from django.db import IntegrityError
-from django.utils.translation import ugettext_lazy as _
-from django.core.exceptions import ValidationError, PermissionDenied
-from django.core.files.uploadedfile import InMemoryUploadedFile
from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.files.uploadedfile import InMemoryUploadedFile
# Transaction
-from django.db import connection
+from django.db import IntegrityError, connection
+from django.utils.translation import ugettext_lazy as _
+from plugins.models import *
+from plugins.validator import validator
+from plugins.views import plugin_notify
+from rpc4django import rpcmethod
+from taggit.models import Tag
-@rpcmethod(name='plugin.maintainers', signature=['string'], login_required=True)
+@rpcmethod(name="plugin.maintainers", signature=["string"], login_required=True)
def plugin_maintaners(**kwargs):
"""
Returns a CSV list of plugin maintainers
"""
- request = kwargs.get('request')
+ request = kwargs.get("request")
if not request.user.is_superuser:
raise PermissionDenied()
- return '\n'.join([u.email for u in User.objects.filter(plugins_created_by__isnull=False, email__isnull=False).exclude(email='').order_by('email').distinct()])
-
-@rpcmethod(name='plugin.upload', signature=['array', 'base64'], login_required=True)
+ return "\n".join(
+ [
+ u.email
+ for u in User.objects.filter(
+ plugins_created_by__isnull=False, email__isnull=False
+ )
+ .exclude(email="")
+ .order_by("email")
+ .distinct()
+ ]
+ )
+
+
+@rpcmethod(name="plugin.upload", signature=["array", "base64"], login_required=True)
def plugin_upload(package, **kwargs):
"""
Creates a new plugin or updates an existing one
@@ -40,48 +48,49 @@ def plugin_upload(package, **kwargs):
"""
try:
- request = kwargs.get('request')
+ request = kwargs.get("request")
package = BytesIO(package)
package.len = package.getbuffer().nbytes
try:
cleaned_data = dict(validator(package))
except ValidationError as e:
- msg = _('File upload must be a valid QGIS Python plugin compressed archive.')
- raise Fault(1, "%s %s" % (msg, ','.join(e.messages)))
+ msg = _(
+ "File upload must be a valid QGIS Python plugin compressed archive."
+ )
+ raise Fault(1, "%s %s" % (msg, ",".join(e.messages)))
plugin_data = {
- 'name' : cleaned_data['name'],
- 'package_name' : cleaned_data['package_name'],
- 'description' : cleaned_data['description'],
- 'created_by' : request.user,
- 'icon' : cleaned_data['icon_file'],
- 'author' : cleaned_data['author'],
- 'email' : cleaned_data['email'],
- 'about' : cleaned_data['about'],
+ "name": cleaned_data["name"],
+ "package_name": cleaned_data["package_name"],
+ "description": cleaned_data["description"],
+ "created_by": request.user,
+ "icon": cleaned_data["icon_file"],
+ "author": cleaned_data["author"],
+ "email": cleaned_data["email"],
+ "about": cleaned_data["about"],
}
-
# Gets existing plugin
try:
- plugin = Plugin.objects.get(package_name=plugin_data['package_name'])
+ plugin = Plugin.objects.get(package_name=plugin_data["package_name"])
# Apply new values
- plugin.name = plugin_data['name']
- plugin.description = plugin_data['description']
- plugin.icon = plugin_data['icon']
+ plugin.name = plugin_data["name"]
+ plugin.description = plugin_data["description"]
+ plugin.icon = plugin_data["icon"]
is_new = False
except Plugin.DoesNotExist:
plugin = Plugin(**plugin_data)
is_new = True
# Optional Metadata:
- if cleaned_data.get('homepage'):
- plugin.homepage = cleaned_data.get('homepage')
- if cleaned_data.get('tracker'):
- plugin.tracker = cleaned_data.get('tracker')
- if cleaned_data.get('repository'):
- plugin.repository = cleaned_data.get('repository')
- if cleaned_data.get('deprecated'):
- plugin.deprecated = cleaned_data.get('deprecated')
+ if cleaned_data.get("homepage"):
+ plugin.homepage = cleaned_data.get("homepage")
+ if cleaned_data.get("tracker"):
+ plugin.tracker = cleaned_data.get("tracker")
+ if cleaned_data.get("repository"):
+ plugin.repository = cleaned_data.get("repository")
+ if cleaned_data.get("deprecated"):
+ plugin.deprecated = cleaned_data.get("deprecated")
plugin.save()
@@ -89,27 +98,34 @@ def plugin_upload(package, **kwargs):
plugin_notify(plugin)
# Takes care of tags
- if cleaned_data.get('tags'):
- plugin.tags.set(*[t.strip().lower() for t in cleaned_data.get('tags').split(',')])
-
- version_data = {
- 'plugin' : plugin,
- 'min_qg_version' : cleaned_data['qgisMinimumVersion'],
- 'version' : cleaned_data['version'],
- 'created_by' : request.user,
- 'package' : InMemoryUploadedFile(package, 'package',
- "%s.zip" % plugin.package_name, 'application/zip',
- package.len, 'UTF-8'),
- 'approved' : request.user.has_perm('plugins.can_approve') or plugin.approved,
+ if cleaned_data.get("tags"):
+ plugin.tags.set(
+ *[t.strip().lower() for t in cleaned_data.get("tags").split(",")]
+ )
+
+ version_data = {
+ "plugin": plugin,
+ "min_qg_version": cleaned_data["qgisMinimumVersion"],
+ "version": cleaned_data["version"],
+ "created_by": request.user,
+ "package": InMemoryUploadedFile(
+ package,
+ "package",
+ "%s.zip" % plugin.package_name,
+ "application/zip",
+ package.len,
+ "UTF-8",
+ ),
+ "approved": request.user.has_perm("plugins.can_approve") or plugin.approved,
}
# Optional version metadata
- if cleaned_data.get('experimental'):
- version_data['experimental'] = cleaned_data.get('experimental')
- if cleaned_data.get('changelog'):
- version_data['changelog'] = cleaned_data.get('changelog')
- if cleaned_data.get('qgisMaximumVersion'):
- version_data['max_qg_version'] = cleaned_data.get('qgisMaximumVersion')
+ if cleaned_data.get("experimental"):
+ version_data["experimental"] = cleaned_data.get("experimental")
+ if cleaned_data.get("changelog"):
+ version_data["changelog"] = cleaned_data.get("changelog")
+ if cleaned_data.get("qgisMaximumVersion"):
+ version_data["max_qg_version"] = cleaned_data.get("qgisMaximumVersion")
new_version = PluginVersion(**version_data)
new_version.clean()
@@ -127,43 +143,64 @@ def plugin_upload(package, **kwargs):
return (plugin.pk, new_version.pk)
-@rpcmethod(name='plugin.tags', signature=['array'], login_required=False)
+@rpcmethod(name="plugin.tags", signature=["array"], login_required=False)
def plugin_tags(**kwargs):
"""
Returns a list of current tags, in alphabetical order
"""
- return [t.name for t in Tag.objects.all().order_by('name')]
-
+ return [t.name for t in Tag.objects.all().order_by("name")]
-@rpcmethod(name='plugin.vote', signature=['array', 'integer', 'integer'], login_required=False)
+@rpcmethod(
+ name="plugin.vote", signature=["array", "integer", "integer"], login_required=False
+)
def plugin_vote(plugin_id, vote, **kwargs):
"""
Vote a plugin, valid values are 1-5
"""
try:
- request = kwargs.get('request')
+ request = kwargs.get("request")
except:
- msg = _('Invalid request.')
+ msg = _("Invalid request.")
raise ValidationError(msg)
try:
plugin = Plugin.objects.get(pk=plugin_id)
except Plugin.DoesNotExist:
- msg = _('Plugin with id %s does not exists.') % plugin_id
+ msg = _("Plugin with id %s does not exists.") % plugin_id
raise ValidationError(msg)
if not int(vote) in range(1, 6):
- msg = _('%s is not a valid vote (1-5).') % vote
+ msg = _("%s is not a valid vote (1-5).") % vote
raise ValidationError(msg)
- cookies=request.COOKIES
+ cookies = request.COOKIES
if request.user.is_anonymous:
# Get the cookie
- cookie_name = "vote-%s.%s.%s" % (ContentType.objects.get(app_label="plugins", model="plugin").pk, plugin_id, plugin.rating.field.key[:6])
+ cookie_name = "vote-%s.%s.%s" % (
+ ContentType.objects.get(app_label="plugins", model="plugin").pk,
+ plugin_id,
+ plugin.rating.field.key[:6],
+ )
if not request.COOKIES.get(cookie_name, False):
# Get the IP
- ip_address = request.META['REMOTE_ADDR']
+ ip_address = request.META["REMOTE_ADDR"]
# Check if a recent vote exists
- rating = plugin.rating.get_ratings().filter(cookie__isnull=False, ip_address=ip_address, date_changed__gte=datetime.datetime.now() - datetime.timedelta(days=10)).order_by('-date_changed')
+ rating = (
+ plugin.rating.get_ratings()
+ .filter(
+ cookie__isnull=False,
+ ip_address=ip_address,
+ date_changed__gte=datetime.datetime.now()
+ - datetime.timedelta(days=10),
+ )
+ .order_by("-date_changed")
+ )
# Change vote if exists
if len(rating):
cookies = {cookie_name: rating[0].cookie}
- return [plugin.rating.add(score=int(vote), user=request.user, ip_address=request.META['REMOTE_ADDR'], cookies=cookies)]
+ return [
+ plugin.rating.add(
+ score=int(vote),
+ user=request.user,
+ ip_address=request.META["REMOTE_ADDR"],
+ cookies=cookies,
+ )
+ ]
diff --git a/qgis-app/plugins/apps.py b/qgis-app/plugins/apps.py
index c26fb50a..f11ac0de 100644
--- a/qgis-app/plugins/apps.py
+++ b/qgis-app/plugins/apps.py
@@ -1,7 +1,8 @@
from django.apps import AppConfig
+
class PluginsConfig(AppConfig):
- name = 'plugins'
+ name = "plugins"
verbose_name = "QGIS Plugins"
def ready(self):
diff --git a/qgis-app/plugins/celery.py b/qgis-app/plugins/celery.py
index 030207ab..3ec72655 100644
--- a/qgis-app/plugins/celery.py
+++ b/qgis-app/plugins/celery.py
@@ -1,17 +1,19 @@
from __future__ import absolute_import
+
import os
+
from celery import Celery
# set the default Django settings module for the 'celery' program.
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings_docker')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings_docker")
-app = Celery('plugins')
+app = Celery("plugins")
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
-app.config_from_object('django.conf:settings', namespace='CELERY')
+app.config_from_object("django.conf:settings", namespace="CELERY")
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
@@ -19,4 +21,4 @@
@app.task(bind=True)
def debug_task(self):
- print('Request: {0!r}'.format(self.request))
+ print("Request: {0!r}".format(self.request))
diff --git a/qgis-app/plugins/forms.py b/qgis-app/plugins/forms.py
index fff5f959..5c10e204 100644
--- a/qgis-app/plugins/forms.py
+++ b/qgis-app/plugins/forms.py
@@ -1,46 +1,59 @@
# i18n
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.auth.models import User
-from django.forms import ModelForm, ValidationError
+import re
+
from django import forms
+from django.contrib.auth.models import User
+from django.forms import CharField, ModelForm, ValidationError
from django.utils.safestring import mark_safe
-from django.forms import CharField
-
+from django.utils.translation import ugettext_lazy as _
+from plugins.models import Plugin, PluginVersion
from plugins.validator import validator
-from plugins.models import PluginVersion, Plugin
from taggit.forms import *
-import re
-
def _clean_tags(tags):
"""Return a stripped and cleaned tag list, empty tags are deleted"""
if tags:
_tags = []
- for t in tags.split(','):
+ for t in tags.split(","):
if t.strip():
- _tags.append(t.strip())
- return ','.join(_tags)
+ _tags.append(t.strip())
+ return ",".join(_tags)
return None
-
class PluginForm(ModelForm):
"""
Form for plugin editing
"""
- required_css_class = 'required'
+
+ required_css_class = "required"
tags = TagField(required=False)
class Meta:
model = Plugin
- fields = ('description', 'about', 'author', 'email', 'icon', 'deprecated', 'homepage', 'tracker', 'repository', 'owners', 'tags', 'server')
+ fields = (
+ "description",
+ "about",
+ "author",
+ "email",
+ "icon",
+ "deprecated",
+ "homepage",
+ "tracker",
+ "repository",
+ "owners",
+ "tags",
+ "server",
+ )
def clean(self):
"""
Check author
"""
- if self.cleaned_data.get('author') and not re.match(r'^[^/]+$', self.cleaned_data.get('author')):
+ if self.cleaned_data.get("author") and not re.match(
+ r"^[^/]+$", self.cleaned_data.get("author")
+ ):
raise ValidationError(_("Author name cannot contain slashes."))
return super(PluginForm, self).clean()
@@ -49,62 +62,97 @@ class PluginVersionForm(ModelForm):
"""
Form for version upload on existing plugins
"""
- required_css_class = 'required'
- changelog = forms.fields.CharField(label=_('Changelog'), required=False,
- help_text=_('Insert here a short description of the changes that have been made in this version. This field is not mandatory and it is automatically filled from the metadata.txt file.'), widget=forms.Textarea)
+ required_css_class = "required"
+ changelog = forms.fields.CharField(
+ label=_("Changelog"),
+ required=False,
+ help_text=_(
+ "Insert here a short description of the changes that have been made in this version. This field is not mandatory and it is automatically filled from the metadata.txt file."
+ ),
+ widget=forms.Textarea,
+ )
def __init__(self, *args, **kwargs):
- is_trusted = kwargs.pop('is_trusted')
+ kwargs.pop("is_trusted")
super(PluginVersionForm, self).__init__(*args, **kwargs)
# FIXME: check why this is not working correctly anymore
# now "approved" is removed from the form (see Meta)
- #instance = getattr(self, 'instance', None)
- #if instance and not is_trusted:
+ # instance = getattr(self, 'instance', None)
+ # if instance and not is_trusted:
# self.fields['approved'].initial = False
# self.fields['approved'].widget.attrs = {'disabled':'disabled'}
# instance.approved = False
-
class Meta:
model = PluginVersion
- exclude = ('created_by', 'plugin', 'approved', 'version', 'min_qg_version', 'max_qg_version')
- fields = ('package', 'experimental', 'changelog')
+ exclude = (
+ "created_by",
+ "plugin",
+ "approved",
+ "version",
+ "min_qg_version",
+ "max_qg_version",
+ )
+ fields = ("package", "experimental", "changelog")
def clean(self):
"""
Only read package if uploaded
"""
# Override package
- changelog = self.cleaned_data.get('changelog')
+ changelog = self.cleaned_data.get("changelog")
if self.files:
- package = self.cleaned_data.get('package')
+ package = self.cleaned_data.get("package")
try:
cleaned_data = validator(package)
- if ('experimental' in dict(cleaned_data)
- and 'experimental' in self.cleaned_data
- and dict(cleaned_data)['experimental'] != self.cleaned_data['experimental']):
- msg = _("The 'experimental' flag in the form does not match the 'experimental' flag in the plugins package metadata.
")
+ if (
+ "experimental" in dict(cleaned_data)
+ and "experimental" in self.cleaned_data
+ and dict(cleaned_data)["experimental"]
+ != self.cleaned_data["experimental"]
+ ):
+ msg = _(
+ "The 'experimental' flag in the form does not match the 'experimental' flag in the plugins package metadata.
"
+ )
raise ValidationError(mark_safe("%s" % msg))
self.cleaned_data.update(cleaned_data)
except ValidationError as e:
- msg = _("There were errors reading plugin package (please check also your plugin's metadata).
")
- raise ValidationError(mark_safe("%s %s" % (msg, '
'.join(e.messages))))
+ msg = _(
+ "There were errors reading plugin package (please check also your plugin's metadata).
"
+ )
+ raise ValidationError(
+ mark_safe("%s %s" % (msg, "
".join(e.messages)))
+ )
# Populate instance
- self.instance.min_qg_version = self.cleaned_data.get('qgisMinimumVersion')
- self.instance.max_qg_version = self.cleaned_data.get('qgisMaximumVersion')
- self.instance.version = PluginVersion.clean_version(self.cleaned_data.get('version'))
- self.instance.server = self.cleaned_data.get('server')
+ self.instance.min_qg_version = self.cleaned_data.get("qgisMinimumVersion")
+ self.instance.max_qg_version = self.cleaned_data.get("qgisMaximumVersion")
+ self.instance.version = PluginVersion.clean_version(
+ self.cleaned_data.get("version")
+ )
+ self.instance.server = self.cleaned_data.get("server")
# Check plugin name
- if self.cleaned_data.get('package_name') and self.cleaned_data.get('package_name') != self.instance.plugin.package_name:
- raise ValidationError(_('Plugin name mismatch: the plugin main folder name in the compressed file (%s) is different from the original plugin package name (%s).') % (self.cleaned_data.get('package_name'), self.instance.plugin.package_name))
+ if (
+ self.cleaned_data.get("package_name")
+ and self.cleaned_data.get("package_name")
+ != self.instance.plugin.package_name
+ ):
+ raise ValidationError(
+ _(
+ "Plugin name mismatch: the plugin main folder name in the compressed file (%s) is different from the original plugin package name (%s)."
+ )
+ % (
+ self.cleaned_data.get("package_name"),
+ self.instance.plugin.package_name,
+ )
+ )
# Also set changelog from metadata
if changelog:
- self.cleaned_data['changelog'] = changelog
+ self.cleaned_data["changelog"] = changelog
# Clean tags
- self.cleaned_data['tags'] = _clean_tags(self.cleaned_data.get('tags', None))
- self.instance.changelog = self.cleaned_data.get('changelog')
+ self.cleaned_data["tags"] = _clean_tags(self.cleaned_data.get("tags", None))
+ self.instance.changelog = self.cleaned_data.get("changelog")
return super(PluginVersionForm, self).clean()
@@ -112,29 +160,51 @@ class PackageUploadForm(forms.Form):
"""
Single step upload for new plugins
"""
- experimental = forms.BooleanField(required=False, label=_('Experimental'), help_text=_('Please check this box if the plugin is experimental. Please note that this field might be overridden by metadata (if present).'))
- package = forms.FileField(label=_('Plugin package'), help_text=_('Please select the zipped file of the plugin.'))
+
+ experimental = forms.BooleanField(
+ required=False,
+ label=_("Experimental"),
+ help_text=_(
+ "Please check this box if the plugin is experimental. Please note that this field might be overridden by metadata (if present)."
+ ),
+ )
+ package = forms.FileField(
+ label=_("Plugin package"),
+ help_text=_("Please select the zipped file of the plugin."),
+ )
def clean_package(self):
"""
Populates cleaned_data with metadata from the zip package
"""
- package = self.cleaned_data.get('package')
+ package = self.cleaned_data.get("package")
try:
self.cleaned_data.update(validator(package))
except ValidationError as e:
- msg = _("There were errors reading plugin package (please check also your plugin's metadata).")
- raise ValidationError(mark_safe("%s %s" % (msg, ','.join(e.messages))))
+ msg = _(
+ "There were errors reading plugin package (please check also your plugin's metadata)."
+ )
+ raise ValidationError(mark_safe("%s %s" % (msg, ",".join(e.messages))))
# Disabled: now the PackageUploadForm also accepts updates
- #if Plugin.objects.filter(package_name = self.cleaned_data['package_name']).count():
+ # if Plugin.objects.filter(package_name = self.cleaned_data['package_name']).count():
# raise ValidationError(_('A plugin with this package name (%s) already exists. To update an existing plugin, you should open the plugin\'s details view and add a new version from there.') % self.cleaned_data['package_name'])
- #if Plugin.objects.filter(name = self.cleaned_data['name']).count():
+ # if Plugin.objects.filter(name = self.cleaned_data['name']).count():
# raise ValidationError(_('A plugin with this name (%s) already exists.') % self.cleaned_data['name'])
- self.cleaned_data['version'] = PluginVersion.clean_version(self.cleaned_data['version'])
+ self.cleaned_data["version"] = PluginVersion.clean_version(
+ self.cleaned_data["version"]
+ )
# Checks for version
- if Plugin.objects.filter(package_name=self.cleaned_data['package_name'], pluginversion__version=self.cleaned_data['version']).count():
- raise ValidationError(_('A plugin with this name (%s) and version number (%s) already exists.') % (self.cleaned_data['name'], self.cleaned_data['version']))
+ if Plugin.objects.filter(
+ package_name=self.cleaned_data["package_name"],
+ pluginversion__version=self.cleaned_data["version"],
+ ).count():
+ raise ValidationError(
+ _(
+ "A plugin with this name (%s) and version number (%s) already exists."
+ )
+ % (self.cleaned_data["name"], self.cleaned_data["version"])
+ )
# Clean tags
- self.cleaned_data['tags'] = _clean_tags(self.cleaned_data.get('tags', None))
+ self.cleaned_data["tags"] = _clean_tags(self.cleaned_data.get("tags", None))
return package
diff --git a/qgis-app/plugins/management/commands/generate_plugins_xml.py b/qgis-app/plugins/management/commands/generate_plugins_xml.py
index 4870fa3f..4efd70e0 100644
--- a/qgis-app/plugins/management/commands/generate_plugins_xml.py
+++ b/qgis-app/plugins/management/commands/generate_plugins_xml.py
@@ -5,17 +5,17 @@
class Command(BaseCommand):
- help = 'Fetch and cached plugins xml'
+ help = "Fetch and cached plugins xml"
def add_arguments(self, parser):
parser.add_argument(
- '-s',
- '--site',
- dest='site',
- default='http://plugins.qgis.org',
- help='Site url to get the source of plugins'
+ "-s",
+ "--site",
+ dest="site",
+ default="http://plugins.qgis.org",
+ help="Site url to get the source of plugins",
)
def handle(self, *args, **options):
- site = options.get('site')
+ site = options.get("site")
generate_plugins_xml.delay(site=site)
diff --git a/qgis-app/plugins/middleware.py b/qgis-app/plugins/middleware.py
index a4e5534d..059a7848 100644
--- a/qgis-app/plugins/middleware.py
+++ b/qgis-app/plugins/middleware.py
@@ -1,21 +1,25 @@
# Custom middleware to handle HTTP_AUTHORIZATION
# Author: A. Pasotti
-from django.contrib.auth.models import User
from django.contrib import auth
+from django.contrib.auth.models import User
def HttpAuthMiddleware(get_response):
"""
Simple HTTP-Basic auth for testing webservices
"""
+
def middleware(request):
- auth_basic = request.META.get('HTTP_AUTHORIZATION')
+ auth_basic = request.META.get("HTTP_AUTHORIZATION")
if auth_basic:
import base64
- username , dummy, password = base64.decodestring(auth_basic[6:].encode('utf8')).partition(b':')
- username = username.decode('utf8')
- password = password.decode('utf8')
+
+ username, dummy, password = base64.decodestring(
+ auth_basic[6:].encode("utf8")
+ ).partition(b":")
+ username = username.decode("utf8")
+ password = password.decode("utf8")
user = auth.authenticate(username=username, password=password)
if user:
@@ -32,4 +36,3 @@ def middleware(request):
return response
return middleware
-
diff --git a/qgis-app/plugins/migrations/0001_initial.py b/qgis-app/plugins/migrations/0001_initial.py
index 885042a7..41119f5b 100644
--- a/qgis-app/plugins/migrations/0001_initial.py
+++ b/qgis-app/plugins/migrations/0001_initial.py
@@ -1,10 +1,10 @@
# Generated by Django 2.1.7 on 2019-03-12 04:30
-from django.conf import settings
-from django.db import migrations, models
import django.db.models.deletion
import plugins.models
import taggit_autosuggest.managers
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -12,65 +12,249 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('taggit', '0001_initial'),
+ ("taggit", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
- name='Plugin',
+ name="Plugin",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')),
- ('modified_on', models.DateTimeField(editable=False, verbose_name='Modified on')),
- ('author', models.CharField(help_text="This is the plugin's original author, if different from the uploader, this field will appear in the XML and in the web GUI", max_length=256, verbose_name='Author')),
- ('email', models.EmailField(max_length=254, verbose_name='Author email')),
- ('homepage', models.URLField(blank=True, null=True, verbose_name='Plugin homepage')),
- ('repository', models.URLField(null=True, verbose_name='Code repository')),
- ('tracker', models.URLField(null=True, verbose_name='Tracker')),
- ('package_name', models.CharField(editable=False, help_text="This is the plugin's internal name, equals to the main folder name", max_length=256, unique=True, verbose_name='Package Name')),
- ('name', models.CharField(help_text='Must be unique', max_length=256, unique=True, verbose_name='Name')),
- ('description', models.TextField(verbose_name='Description')),
- ('about', models.TextField(null=True, verbose_name='About')),
- ('icon', models.ImageField(blank=True, null=True, upload_to='packages/%Y', verbose_name='Icon')),
- ('downloads', models.IntegerField(default=0, editable=False, verbose_name='Downloads')),
- ('featured', models.BooleanField(db_index=True, default=False, verbose_name='Featured')),
- ('deprecated', models.BooleanField(db_index=True, default=False, verbose_name='Deprecated')),
- ('server', models.BooleanField(db_index=True, default=False, verbose_name='Server')),
- ('rating_votes', models.PositiveIntegerField(blank=True, default=0, editable=False)),
- ('rating_score', models.IntegerField(blank=True, default=0, editable=False)),
- ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plugins_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
- ('owners', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)),
- ('tags', taggit_autosuggest.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "created_on",
+ models.DateTimeField(auto_now_add=True, verbose_name="Created on"),
+ ),
+ (
+ "modified_on",
+ models.DateTimeField(editable=False, verbose_name="Modified on"),
+ ),
+ (
+ "author",
+ models.CharField(
+ help_text="This is the plugin's original author, if different from the uploader, this field will appear in the XML and in the web GUI",
+ max_length=256,
+ verbose_name="Author",
+ ),
+ ),
+ (
+ "email",
+ models.EmailField(max_length=254, verbose_name="Author email"),
+ ),
+ (
+ "homepage",
+ models.URLField(
+ blank=True, null=True, verbose_name="Plugin homepage"
+ ),
+ ),
+ (
+ "repository",
+ models.URLField(null=True, verbose_name="Code repository"),
+ ),
+ ("tracker", models.URLField(null=True, verbose_name="Tracker")),
+ (
+ "package_name",
+ models.CharField(
+ editable=False,
+ help_text="This is the plugin's internal name, equals to the main folder name",
+ max_length=256,
+ unique=True,
+ verbose_name="Package Name",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ help_text="Must be unique",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
+ ),
+ ("description", models.TextField(verbose_name="Description")),
+ ("about", models.TextField(null=True, verbose_name="About")),
+ (
+ "icon",
+ models.ImageField(
+ blank=True,
+ null=True,
+ upload_to="packages/%Y",
+ verbose_name="Icon",
+ ),
+ ),
+ (
+ "downloads",
+ models.IntegerField(
+ default=0, editable=False, verbose_name="Downloads"
+ ),
+ ),
+ (
+ "featured",
+ models.BooleanField(
+ db_index=True, default=False, verbose_name="Featured"
+ ),
+ ),
+ (
+ "deprecated",
+ models.BooleanField(
+ db_index=True, default=False, verbose_name="Deprecated"
+ ),
+ ),
+ (
+ "server",
+ models.BooleanField(
+ db_index=True, default=False, verbose_name="Server"
+ ),
+ ),
+ (
+ "rating_votes",
+ models.PositiveIntegerField(blank=True, default=0, editable=False),
+ ),
+ (
+ "rating_score",
+ models.IntegerField(blank=True, default=0, editable=False),
+ ),
+ (
+ "created_by",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="plugins_created_by",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
+ ),
+ (
+ "owners",
+ models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL),
+ ),
+ (
+ "tags",
+ taggit_autosuggest.managers.TaggableManager(
+ blank=True,
+ help_text="A comma-separated list of tags.",
+ through="taggit.TaggedItem",
+ to="taggit.Tag",
+ verbose_name="Tags",
+ ),
+ ),
],
options={
- 'ordering': ('name',),
- 'permissions': (('can_approve', 'Can approve plugins versions'),),
+ "ordering": ("name",),
+ "permissions": (("can_approve", "Can approve plugins versions"),),
},
),
migrations.CreateModel(
- name='PluginVersion',
+ name="PluginVersion",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')),
- ('downloads', models.IntegerField(default=0, editable=False, verbose_name='Downloads')),
- ('min_qg_version', plugins.models.QGVersionZeroForcedField(db_index=True, max_length=32, verbose_name='Minimum QGIS version')),
- ('max_qg_version', plugins.models.QGVersionZeroForcedField(blank=True, db_index=True, max_length=32, null=True, verbose_name='Maximum QGIS version')),
- ('version', plugins.models.VersionField(db_index=True, max_length=32, verbose_name='Version')),
- ('changelog', models.TextField(blank=True, null=True, verbose_name='Changelog')),
- ('package', models.FileField(upload_to='packages/%Y', verbose_name='Plugin package')),
- ('experimental', models.BooleanField(db_index=True, default=False, help_text="Check this box if this version is experimental, leave unchecked if it's stable. Please note that this field might be overridden by metadata (if present).", verbose_name='Experimental flag')),
- ('approved', models.BooleanField(db_index=True, default=True, help_text='Set to false if you wish to unapprove the plugin version.', verbose_name='Approved')),
- ('external_deps', models.CharField(help_text='PIP install string', max_length=512, null=True, verbose_name='External dependencies')),
- ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
- ('plugin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='plugins.Plugin')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "created_on",
+ models.DateTimeField(auto_now_add=True, verbose_name="Created on"),
+ ),
+ (
+ "downloads",
+ models.IntegerField(
+ default=0, editable=False, verbose_name="Downloads"
+ ),
+ ),
+ (
+ "min_qg_version",
+ plugins.models.QGVersionZeroForcedField(
+ db_index=True,
+ max_length=32,
+ verbose_name="Minimum QGIS version",
+ ),
+ ),
+ (
+ "max_qg_version",
+ plugins.models.QGVersionZeroForcedField(
+ blank=True,
+ db_index=True,
+ max_length=32,
+ null=True,
+ verbose_name="Maximum QGIS version",
+ ),
+ ),
+ (
+ "version",
+ plugins.models.VersionField(
+ db_index=True, max_length=32, verbose_name="Version"
+ ),
+ ),
+ (
+ "changelog",
+ models.TextField(blank=True, null=True, verbose_name="Changelog"),
+ ),
+ (
+ "package",
+ models.FileField(
+ upload_to="packages/%Y", verbose_name="Plugin package"
+ ),
+ ),
+ (
+ "experimental",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Check this box if this version is experimental, leave unchecked if it's stable. Please note that this field might be overridden by metadata (if present).",
+ verbose_name="Experimental flag",
+ ),
+ ),
+ (
+ "approved",
+ models.BooleanField(
+ db_index=True,
+ default=True,
+ help_text="Set to false if you wish to unapprove the plugin version.",
+ verbose_name="Approved",
+ ),
+ ),
+ (
+ "external_deps",
+ models.CharField(
+ help_text="PIP install string",
+ max_length=512,
+ null=True,
+ verbose_name="External dependencies",
+ ),
+ ),
+ (
+ "created_by",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
+ ),
+ (
+ "plugin",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="plugins.Plugin"
+ ),
+ ),
],
options={
- 'ordering': ('plugin', '-version', 'experimental'),
+ "ordering": ("plugin", "-version", "experimental"),
},
),
migrations.AlterUniqueTogether(
- name='pluginversion',
- unique_together={('plugin', 'version')},
+ name="pluginversion",
+ unique_together={("plugin", "version")},
),
]
diff --git a/qgis-app/plugins/models.py b/qgis-app/plugins/models.py
index f0e13322..a37ea761 100644
--- a/qgis-app/plugins/models.py
+++ b/qgis-app/plugins/models.py
@@ -12,13 +12,13 @@
from djangoratings.fields import AnonymousRatingField
from taggit_autosuggest.managers import TaggableManager
-PLUGINS_STORAGE_PATH = getattr(settings, 'PLUGINS_STORAGE_PATH', 'packages/%Y')
-PLUGINS_FRESH_DAYS = getattr(settings, 'PLUGINS_FRESH_DAYS', 30)
+PLUGINS_STORAGE_PATH = getattr(settings, "PLUGINS_STORAGE_PATH", "packages/%Y")
+PLUGINS_FRESH_DAYS = getattr(settings, "PLUGINS_FRESH_DAYS", 30)
# Used in Version fields to transform DB value back to human readable string
# Allows "-" for processing plugin
-VERSION_RE = r'(^|(?<=\.))0+(?!(\.|$|-))|\.#+'
+VERSION_RE = r"(^|(?<=\.))0+(?!(\.|$|-))|\.#+"
class BasePluginManager(models.Manager):
@@ -27,16 +27,21 @@ class BasePluginManager(models.Manager):
"""
def get_queryset(self):
- return super(BasePluginManager, self).get_queryset().extra(
- select={
- 'average_vote': 'rating_score/(rating_votes+0.001)',
- 'latest_version_date': (
- 'SELECT created_on FROM plugins_pluginversion WHERE '
- 'plugins_pluginversion.plugin_id = plugins_plugin.id '
- 'AND approved = TRUE '
- 'ORDER BY created_on DESC LIMIT 1'
- )
- })
+ return (
+ super(BasePluginManager, self)
+ .get_queryset()
+ .extra(
+ select={
+ "average_vote": "rating_score/(rating_votes+0.001)",
+ "latest_version_date": (
+ "SELECT created_on FROM plugins_pluginversion WHERE "
+ "plugins_pluginversion.plugin_id = plugins_plugin.id "
+ "AND approved = TRUE "
+ "ORDER BY created_on DESC LIMIT 1"
+ ),
+ }
+ )
+ )
class ApprovedPlugins(BasePluginManager):
@@ -46,7 +51,12 @@ class ApprovedPlugins(BasePluginManager):
"""
def get_queryset(self):
- return super(ApprovedPlugins, self).get_queryset().filter(pluginversion__approved=True).distinct()
+ return (
+ super(ApprovedPlugins, self)
+ .get_queryset()
+ .filter(pluginversion__approved=True)
+ .distinct()
+ )
class StablePlugins(BasePluginManager):
@@ -56,7 +66,12 @@ class StablePlugins(BasePluginManager):
"""
def get_queryset(self):
- return super(StablePlugins, self).get_queryset().filter(pluginversion__approved=True, pluginversion__experimental=False).distinct()
+ return (
+ super(StablePlugins, self)
+ .get_queryset()
+ .filter(pluginversion__approved=True, pluginversion__experimental=False)
+ .distinct()
+ )
class ExperimentalPlugins(BasePluginManager):
@@ -66,7 +81,12 @@ class ExperimentalPlugins(BasePluginManager):
"""
def get_queryset(self):
- return super(ExperimentalPlugins, self).get_queryset().filter(pluginversion__approved=True, pluginversion__experimental=True).distinct()
+ return (
+ super(ExperimentalPlugins, self)
+ .get_queryset()
+ .filter(pluginversion__approved=True, pluginversion__experimental=True)
+ .distinct()
+ )
class FeaturedPlugins(BasePluginManager):
@@ -76,7 +96,13 @@ class FeaturedPlugins(BasePluginManager):
"""
def get_queryset(self):
- return super(FeaturedPlugins, self).get_queryset().filter(pluginversion__approved=True, featured=True).order_by('-created_on').distinct()
+ return (
+ super(FeaturedPlugins, self)
+ .get_queryset()
+ .filter(pluginversion__approved=True, featured=True)
+ .order_by("-created_on")
+ .distinct()
+ )
class FreshPlugins(BasePluginManager):
@@ -90,11 +116,18 @@ def __init__(self, days=PLUGINS_FRESH_DAYS, *args, **kwargs):
return super(FreshPlugins, self).__init__(*args, **kwargs)
def get_queryset(self):
- return super(FreshPlugins, self).get_queryset().filter(
- deprecated=False,
- pluginversion__approved=True,
- created_on__gte=datetime.datetime.now() - datetime.timedelta(days=self.days)
- ).order_by('-created_on').distinct()
+ return (
+ super(FreshPlugins, self)
+ .get_queryset()
+ .filter(
+ deprecated=False,
+ pluginversion__approved=True,
+ created_on__gte=datetime.datetime.now()
+ - datetime.timedelta(days=self.days),
+ )
+ .order_by("-created_on")
+ .distinct()
+ )
class LatestPlugins(BasePluginManager):
@@ -108,12 +141,19 @@ def __init__(self, days=PLUGINS_FRESH_DAYS, *args, **kwargs):
return super(LatestPlugins, self).__init__(*args, **kwargs)
def get_queryset(self):
- return super(LatestPlugins, self).get_queryset().filter(
- deprecated=False,
- pluginversion__approved=True,
- pluginversion__created_on__gte=(
- datetime.datetime.now() - datetime.timedelta(days=self.days))
- ).order_by('-latest_version_date').distinct()
+ return (
+ super(LatestPlugins, self)
+ .get_queryset()
+ .filter(
+ deprecated=False,
+ pluginversion__approved=True,
+ pluginversion__created_on__gte=(
+ datetime.datetime.now() - datetime.timedelta(days=self.days)
+ ),
+ )
+ .order_by("-latest_version_date")
+ .distinct()
+ )
class UnapprovedPlugins(BasePluginManager):
@@ -122,7 +162,12 @@ class UnapprovedPlugins(BasePluginManager):
"""
def get_queryset(self):
- return super(UnapprovedPlugins, self).get_queryset().filter(pluginversion__approved=False, deprecated=False).distinct()
+ return (
+ super(UnapprovedPlugins, self)
+ .get_queryset()
+ .filter(pluginversion__approved=False, deprecated=False)
+ .distinct()
+ )
class DeprecatedPlugins(BasePluginManager):
@@ -131,7 +176,12 @@ class DeprecatedPlugins(BasePluginManager):
"""
def get_queryset(self):
- return super(DeprecatedPlugins, self).get_queryset().filter(deprecated=True).distinct()
+ return (
+ super(DeprecatedPlugins, self)
+ .get_queryset()
+ .filter(deprecated=True)
+ .distinct()
+ )
class PopularPlugins(ApprovedPlugins):
@@ -140,11 +190,18 @@ class PopularPlugins(ApprovedPlugins):
"""
def get_queryset(self):
- return super(PopularPlugins, self).get_queryset().filter(deprecated=False).extra(
- select={
- 'popularity': 'plugins_plugin.downloads * (1 + (rating_score/(rating_votes+0.01)/3))'
- }
- ).order_by('-popularity').distinct()
+ return (
+ super(PopularPlugins, self)
+ .get_queryset()
+ .filter(deprecated=False)
+ .extra(
+ select={
+ "popularity": "plugins_plugin.downloads * (1 + (rating_score/(rating_votes+0.01)/3))"
+ }
+ )
+ .order_by("-popularity")
+ .distinct()
+ )
class MostDownloadedPlugins(ApprovedPlugins):
@@ -153,7 +210,13 @@ class MostDownloadedPlugins(ApprovedPlugins):
"""
def get_queryset(self):
- return super(MostDownloadedPlugins, self).get_queryset().filter(deprecated=False).order_by('-downloads').distinct()
+ return (
+ super(MostDownloadedPlugins, self)
+ .get_queryset()
+ .filter(deprecated=False)
+ .order_by("-downloads")
+ .distinct()
+ )
class MostVotedPlugins(ApprovedPlugins):
@@ -162,7 +225,13 @@ class MostVotedPlugins(ApprovedPlugins):
"""
def get_queryset(self):
- return super(MostVotedPlugins, self).get_queryset().filter(deprecated=False).order_by('-rating_votes').distinct()
+ return (
+ super(MostVotedPlugins, self)
+ .get_queryset()
+ .filter(deprecated=False)
+ .order_by("-rating_votes")
+ .distinct()
+ )
class MostRatedPlugins(ApprovedPlugins):
@@ -171,7 +240,13 @@ class MostRatedPlugins(ApprovedPlugins):
"""
def get_queryset(self):
- return super(MostRatedPlugins, self).get_queryset().filter(deprecated=False).order_by('-average_vote').distinct()
+ return (
+ super(MostRatedPlugins, self)
+ .get_queryset()
+ .filter(deprecated=False)
+ .order_by("-average_vote")
+ .distinct()
+ )
class TaggablePlugins(TaggableManager):
@@ -180,7 +255,12 @@ class TaggablePlugins(TaggableManager):
"""
def get_queryset(self):
- return super(TaggablePlugins, self).get_queryset().filter(deprecated=False, pluginversion__approved=True).distinct()
+ return (
+ super(TaggablePlugins, self)
+ .get_queryset()
+ .filter(deprecated=False, pluginversion__approved=True)
+ .distinct()
+ )
class ServerPlugins(ApprovedPlugins):
@@ -192,50 +272,68 @@ def get_queryset(self):
return super(ServerPlugins, self).get_queryset().filter(server=True).distinct()
-class Plugin (models.Model):
+class Plugin(models.Model):
"""
Plugins model
"""
# dates
created_on = models.DateTimeField(
- _('Created on'), auto_now_add=True, editable=False)
- modified_on = models.DateTimeField(_('Modified on'), editable=False)
+ _("Created on"), auto_now_add=True, editable=False
+ )
+ modified_on = models.DateTimeField(_("Modified on"), editable=False)
# owners
- created_by = models.ForeignKey(User, verbose_name=_(
- 'Created by'), related_name='plugins_created_by', on_delete=models.CASCADE)
- author = models.CharField(_('Author'), help_text=_(
- 'This is the plugin\'s original author, if different from the uploader, this field will appear in the XML and in the web GUI'), max_length=256)
- email = models.EmailField(_('Author email'))
- homepage = models.URLField(_('Plugin homepage'), blank=True, null=True)
+ created_by = models.ForeignKey(
+ User,
+ verbose_name=_("Created by"),
+ related_name="plugins_created_by",
+ on_delete=models.CASCADE,
+ )
+ author = models.CharField(
+ _("Author"),
+ help_text=_(
+ "This is the plugin's original author, if different from the uploader, this field will appear in the XML and in the web GUI"
+ ),
+ max_length=256,
+ )
+ email = models.EmailField(_("Author email"))
+ homepage = models.URLField(_("Plugin homepage"), blank=True, null=True)
# Support
- repository = models.URLField(_('Code repository'), blank=False, null=True)
- tracker = models.URLField(_('Tracker'), blank=False, null=True)
+ repository = models.URLField(_("Code repository"), blank=False, null=True)
+ tracker = models.URLField(_("Tracker"), blank=False, null=True)
owners = models.ManyToManyField(User, blank=True)
# name, desc etc.
- package_name = models.CharField(_('Package Name'), help_text=_(
- 'This is the plugin\'s internal name, equals to the main folder name'), max_length=256, unique=True, editable=False)
- name = models.CharField(_('Name'), help_text=_(
- 'Must be unique'), max_length=256, unique=True)
- description = models.TextField(_('Description'))
- about = models.TextField(_('About'), blank=False, null=True)
-
- icon = models.ImageField(_('Icon'), blank=True,
- null=True, upload_to=PLUGINS_STORAGE_PATH)
+ package_name = models.CharField(
+ _("Package Name"),
+ help_text=_(
+ "This is the plugin's internal name, equals to the main folder name"
+ ),
+ max_length=256,
+ unique=True,
+ editable=False,
+ )
+ name = models.CharField(
+ _("Name"), help_text=_("Must be unique"), max_length=256, unique=True
+ )
+ description = models.TextField(_("Description"))
+ about = models.TextField(_("About"), blank=False, null=True)
+
+ icon = models.ImageField(
+ _("Icon"), blank=True, null=True, upload_to=PLUGINS_STORAGE_PATH
+ )
# downloads (soft trigger from versions)
- downloads = models.IntegerField(_('Downloads'), default=0, editable=False)
+ downloads = models.IntegerField(_("Downloads"), default=0, editable=False)
# Flags
- featured = models.BooleanField(_('Featured'), default=False, db_index=True)
- deprecated = models.BooleanField(
- _('Deprecated'), default=False, db_index=True)
+ featured = models.BooleanField(_("Featured"), default=False, db_index=True)
+ deprecated = models.BooleanField(_("Deprecated"), default=False, db_index=True)
# True if the plugin has a server interface
- server = models.BooleanField(_('Server'), default=False, db_index=True)
+ server = models.BooleanField(_("Server"), default=False, db_index=True)
# Managers
objects = models.Manager()
@@ -255,7 +353,8 @@ class Plugin (models.Model):
server_objects = ServerPlugins()
rating = AnonymousRatingField(
- range=5, use_cookies=True, can_change_vote=True, allow_delete=True)
+ range=5, use_cookies=True, can_change_vote=True, allow_delete=True
+ )
tags = TaggableManager(blank=True)
@@ -272,7 +371,7 @@ def trusted(self):
Returns True if the plugin's author has plugins.can_approve permission
Purpose of this decorator is to show/hide buttons in the template
"""
- return self.created_by.has_perm('plugins.can_approve')
+ return self.created_by.has_perm("plugins.can_approve")
@property
def stable(self):
@@ -280,7 +379,9 @@ def stable(self):
Returns the latest stable and approved version
"""
try:
- return self.pluginversion_set.filter(approved=True, experimental=False).order_by('-version')[0]
+ return self.pluginversion_set.filter(
+ approved=True, experimental=False
+ ).order_by("-version")[0]
except:
return None
@@ -290,7 +391,9 @@ def experimental(self):
Returns the latest experimental and approved version
"""
try:
- return self.pluginversion_set.filter(approved=True, experimental=True).order_by('-version')[0]
+ return self.pluginversion_set.filter(
+ approved=True, experimental=True
+ ).order_by("-version")[0]
except:
return None
@@ -308,7 +411,7 @@ def approvers(self):
"""
Returns a list of editor users that can approve a version
"""
- return [l for l in self.editors if l.has_perm('plugins.can_approve')]
+ return [l for l in self.editors if l.has_perm("plugins.can_approve")]
@property
def avg_vote(self):
@@ -319,21 +422,19 @@ def avg_vote(self):
This property is still useful when the object is not loaded
through a manager, for example in related objects.
"""
- return self.rating_score/(self.rating_votes+0.001)
+ return self.rating_score / (self.rating_votes + 0.001)
class Meta:
- ordering = ('name',)
+ ordering = ("name",)
# ABP: Note: this permission should belong to the
# PluginVersion class. I left it here because it
# doesn't really matters where it is. Just be
# sure you query for it using the 'plugins' class
# instead of the 'pluginversion' class.
- permissions = (
- ("can_approve", "Can approve plugins versions"),
- )
+ permissions = (("can_approve", "Can approve plugins versions"),)
def get_absolute_url(self):
- return reverse('plugin_detail', args=(self.package_name,))
+ return reverse("plugin_detail", args=(self.package_name,))
def __unicode__(self):
return "[%s] %s" % (self.pk, self.name)
@@ -350,27 +451,38 @@ def clean(self):
"""
from django.core.exceptions import ValidationError
- if not re.match(r'^[A-Za-z][A-Za-z0-9-_]+$', self.package_name):
+ if not re.match(r"^[A-Za-z][A-Za-z0-9-_]+$", self.package_name):
raise ValidationError(
- _('Plugin package_name (which equals to the main plugin folder inside the zip file) must start with an ASCII letter and can contain only ASCII letters, digits and the - and _ signs.'))
+ _(
+ "Plugin package_name (which equals to the main plugin folder inside the zip file) must start with an ASCII letter and can contain only ASCII letters, digits and the - and _ signs."
+ )
+ )
if self.pk:
- qs = Plugin.objects.filter(
- name__iexact=self.name).exclude(pk=self.pk)
+ qs = Plugin.objects.filter(name__iexact=self.name).exclude(pk=self.pk)
else:
qs = Plugin.objects.filter(name__iexact=self.name)
if qs.count():
raise ValidationError(
- _('A plugin with a similar name (%s) already exists (the name only differs in case).') % qs.all()[0].name)
+ _(
+ "A plugin with a similar name (%s) already exists (the name only differs in case)."
+ )
+ % qs.all()[0].name
+ )
if self.pk:
- qs = Plugin.objects.filter(
- package_name__iexact=self.package_name).exclude(pk=self.pk)
+ qs = Plugin.objects.filter(package_name__iexact=self.package_name).exclude(
+ pk=self.pk
+ )
else:
qs = Plugin.objects.filter(package_name__iexact=self.package_name)
if qs.count():
raise ValidationError(
- _('A plugin with a similar package_name (%s) already exists (the package_name only differs in case).') % qs.all()[0].package_name)
+ _(
+ "A plugin with a similar package_name (%s) already exists (the package_name only differs in case)."
+ )
+ % qs.all()[0].package_name
+ )
def save(self, keep_date=False, *args, **kwargs):
"""
@@ -379,7 +491,8 @@ def save(self, keep_date=False, *args, **kwargs):
"""
if self.pk and not keep_date:
import logging
- logging.debug('Updating modified_on for the Plugin instance')
+
+ logging.debug("Updating modified_on for the Plugin instance")
self.modified_on = datetime.datetime.now()
if not self.pk:
self.modified_on = datetime.datetime.now()
@@ -395,7 +508,12 @@ class ApprovedPluginVersions(models.Manager):
"""
def get_queryset(self):
- return super(ApprovedPluginVersions, self).get_queryset().filter(approved=True).order_by('-version')
+ return (
+ super(ApprovedPluginVersions, self)
+ .get_queryset()
+ .filter(approved=True)
+ .order_by("-version")
+ )
class StablePluginVersions(ApprovedPluginVersions):
@@ -405,7 +523,9 @@ class StablePluginVersions(ApprovedPluginVersions):
"""
def get_queryset(self):
- return super(StablePluginVersions, self).get_queryset().filter(experimental=False)
+ return (
+ super(StablePluginVersions, self).get_queryset().filter(experimental=False)
+ )
class ExperimentalPluginVersions(ApprovedPluginVersions):
@@ -415,10 +535,14 @@ class ExperimentalPluginVersions(ApprovedPluginVersions):
"""
def get_queryset(self):
- return super(ExperimentalPluginVersions, self).get_queryset().filter(experimental=True)
+ return (
+ super(ExperimentalPluginVersions, self)
+ .get_queryset()
+ .filter(experimental=True)
+ )
-def vjust(str, level=3, delim='.', bitsize=3, fillchar=' ', force_zero=False):
+def vjust(str, level=3, delim=".", bitsize=3, fillchar=" ", force_zero=False):
"""
Normalize a dotted version string.
@@ -438,13 +562,13 @@ def vjust(str, level=3, delim='.', bitsize=3, fillchar=' ', force_zero=False):
nb = str.count(delim)
if nb < level:
if force_zero:
- str += (level-nb) * (delim+'0')
+ str += (level - nb) * (delim + "0")
else:
- str += (level-nb) * delim
+ str += (level - nb) * delim
parts = []
- for v in str.split(delim)[:level+1]:
+ for v in str.split(delim)[: level + 1]:
if not v:
- parts.append(v.rjust(bitsize, '#'))
+ parts.append(v.rjust(bitsize, "#"))
else:
parts.append(v.rjust(bitsize, fillchar))
return delim.join(parts)
@@ -455,12 +579,12 @@ class VersionField(models.CharField):
description = 'Field to store version strings ("a.b.c.d") in a way it is sortable'
def get_prep_value(self, value):
- return vjust(value, fillchar='0')
+ return vjust(value, fillchar="0")
def to_python(self, value):
if not value:
- return ''
- return re.sub(VERSION_RE, '', value)
+ return ""
+ return re.sub(VERSION_RE, "", value)
def from_db_value(self, value, expression, connection):
if value is None:
@@ -474,12 +598,12 @@ class QGVersionZeroForcedField(models.CharField):
is sortable and QGIS scheme compatible (x.y.z).'
def get_prep_value(self, value):
- return vjust(value, fillchar='0', level=2, force_zero=True)
+ return vjust(value, fillchar="0", level=2, force_zero=True)
def to_python(self, value):
if not value:
- return ''
- return re.sub(VERSION_RE, '', value)
+ return ""
+ return re.sub(VERSION_RE, "", value)
def from_db_value(self, value, expression, connection):
if value is None:
@@ -487,7 +611,7 @@ def from_db_value(self, value, expression, connection):
return self.to_python(value)
-class PluginVersion (models.Model):
+class PluginVersion(models.Model):
"""
Plugin versions
"""
@@ -496,30 +620,48 @@ class PluginVersion (models.Model):
plugin = models.ForeignKey(Plugin, on_delete=models.CASCADE)
# dates
created_on = models.DateTimeField(
- _('Created on'), auto_now_add=True, editable=False)
+ _("Created on"), auto_now_add=True, editable=False
+ )
# download counter
- downloads = models.IntegerField(_('Downloads'), default=0, editable=False)
+ downloads = models.IntegerField(_("Downloads"), default=0, editable=False)
# owners
- created_by = models.ForeignKey(User, verbose_name=_(
- 'Created by'), on_delete=models.CASCADE)
+ created_by = models.ForeignKey(
+ User, verbose_name=_("Created by"), on_delete=models.CASCADE
+ )
# version info, the first should be read from plugin
min_qg_version = QGVersionZeroForcedField(
- _('Minimum QGIS version'), max_length=32, db_index=True)
+ _("Minimum QGIS version"), max_length=32, db_index=True
+ )
max_qg_version = QGVersionZeroForcedField(
- _('Maximum QGIS version'), max_length=32, null=True, blank=True, db_index=True)
- version = VersionField(_('Version'), max_length=32, db_index=True)
- changelog = models.TextField(_('Changelog'), null=True, blank=True)
+ _("Maximum QGIS version"), max_length=32, null=True, blank=True, db_index=True
+ )
+ version = VersionField(_("Version"), max_length=32, db_index=True)
+ changelog = models.TextField(_("Changelog"), null=True, blank=True)
# the file!
- package = models.FileField(
- _('Plugin package'), upload_to=PLUGINS_STORAGE_PATH)
+ package = models.FileField(_("Plugin package"), upload_to=PLUGINS_STORAGE_PATH)
# Flags: checks on unique current/experimental are done in save() and possibly in the views
- experimental = models.BooleanField(_('Experimental flag'), default=False, help_text=_(
- "Check this box if this version is experimental, leave unchecked if it's stable. Please note that this field might be overridden by metadata (if present)."), db_index=True)
- approved = models.BooleanField(_('Approved'), default=True, help_text=_(
- 'Set to false if you wish to unapprove the plugin version.'), db_index=True)
- external_deps = models.CharField(_('External dependencies'), help_text=_(
- 'PIP install string'), max_length=512, blank=False, null=True)
+ experimental = models.BooleanField(
+ _("Experimental flag"),
+ default=False,
+ help_text=_(
+ "Check this box if this version is experimental, leave unchecked if it's stable. Please note that this field might be overridden by metadata (if present)."
+ ),
+ db_index=True,
+ )
+ approved = models.BooleanField(
+ _("Approved"),
+ default=True,
+ help_text=_("Set to false if you wish to unapprove the plugin version."),
+ db_index=True,
+ )
+ external_deps = models.CharField(
+ _("External dependencies"),
+ help_text=_("PIP install string"),
+ max_length=512,
+ blank=False,
+ null=True,
+ )
# Managers, used in xml output
objects = models.Manager()
@@ -539,8 +681,8 @@ def save(self, *args, **kwargs):
# Transforms the version...
# Need to be done here too, because clean()
# is only called in forms.
- if self.version.rfind(' ') > 0:
- self.version = self.version.rsplit(' ')[-1]
+ if self.version.rfind(" ") > 0:
+ self.version = self.version.rsplit(" ")[-1]
# Only change modified_on when a new version is created,
# every download triggers a save to update the counter
@@ -550,8 +692,7 @@ def save(self, *args, **kwargs):
# fix Max version
if not self.max_qg_version:
- self.max_qg_version = "%s.99" % tuple(
- self.min_qg_version.split('.')[0])
+ self.max_qg_version = "%s.99" % tuple(self.min_qg_version.split(".")[0])
super(PluginVersion, self).save(*args, **kwargs)
@@ -568,32 +709,51 @@ def clean(self):
self.version = PluginVersion.clean_version(self.version)
versions_to_check = PluginVersion.objects.filter(
- plugin=self.plugin, version=self.version)
+ plugin=self.plugin, version=self.version
+ )
if self.pk:
versions_to_check = versions_to_check.exclude(pk=self.pk)
# Checks for unique_together
- if versions_to_check.filter(plugin=self.plugin, version=self.version).count() > 0:
+ if (
+ versions_to_check.filter(plugin=self.plugin, version=self.version).count()
+ > 0
+ ):
raise ValidationError(
- _('Version value must be unique among each plugin: a version with same number already exists.'))
+ _(
+ "Version value must be unique among each plugin: a version with same number already exists."
+ )
+ )
@staticmethod
def clean_version(version):
"""
Strips blanks and Version string
"""
- if version.rfind(' ') > 0:
- version = version.rsplit(' ')[-1]
+ if version.rfind(" ") > 0:
+ version = version.rsplit(" ")[-1]
return version
class Meta:
- unique_together = ('plugin', 'version')
- ordering = ('plugin', '-version', 'experimental')
+ unique_together = ("plugin", "version")
+ ordering = ("plugin", "-version", "experimental")
def get_absolute_url(self):
- return reverse('version_detail', args=(self.plugin.package_name, self.version,))
+ return reverse(
+ "version_detail",
+ args=(
+ self.plugin.package_name,
+ self.version,
+ ),
+ )
def get_download_url(self):
- return reverse('version_download', args=(self.plugin.package_name, self.version,))
+ return reverse(
+ "version_download",
+ args=(
+ self.plugin.package_name,
+ self.version,
+ ),
+ )
def download_file_name(self):
return "%s.%s.zip" % (self.plugin.package_name, self.version)
@@ -601,7 +761,7 @@ def download_file_name(self):
def __unicode__(self):
desc = "%s %s" % (self.plugin, self.version)
if self.experimental:
- desc = "%s %s" % (desc, _('Experimental'))
+ desc = "%s %s" % (desc, _("Experimental"))
return desc
def __str__(self):
@@ -628,6 +788,5 @@ def delete_plugin_icon(sender, instance, **kw):
pass
-models.signals.post_delete.connect(
- delete_version_package, sender=PluginVersion)
+models.signals.post_delete.connect(delete_version_package, sender=PluginVersion)
models.signals.post_delete.connect(delete_plugin_icon, sender=Plugin)
diff --git a/qgis-app/plugins/search_indexes.py b/qgis-app/plugins/search_indexes.py
index 35a49e1f..889be9f1 100644
--- a/qgis-app/plugins/search_indexes.py
+++ b/qgis-app/plugins/search_indexes.py
@@ -4,13 +4,13 @@
class PluginIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
- created_by = indexes.CharField(model_attr='created_by')
- created_on = indexes.DateTimeField(model_attr='created_on')
+ created_by = indexes.CharField(model_attr="created_by")
+ created_on = indexes.DateTimeField(model_attr="created_on")
# We add this for autocomplete.
- name_auto = indexes.EdgeNgramField(model_attr='name')
- description_auto = indexes.EdgeNgramField(model_attr='description')
- about_auto = indexes.EdgeNgramField(model_attr='about', default='')
- package_name_auto = indexes.EdgeNgramField(model_attr='package_name', default='')
+ name_auto = indexes.EdgeNgramField(model_attr="name")
+ description_auto = indexes.EdgeNgramField(model_attr="description")
+ about_auto = indexes.EdgeNgramField(model_attr="about", default="")
+ package_name_auto = indexes.EdgeNgramField(model_attr="package_name", default="")
def get_model(self):
return Plugin
diff --git a/qgis-app/plugins/tasks/__init__.py b/qgis-app/plugins/tasks/__init__.py
index c06d2250..7987374c 100644
--- a/qgis-app/plugins/tasks/__init__.py
+++ b/qgis-app/plugins/tasks/__init__.py
@@ -1 +1 @@
-from plugins.tasks.generate_plugins_xml import *
\ No newline at end of file
+from plugins.tasks.generate_plugins_xml import *
diff --git a/qgis-app/plugins/tasks/generate_plugins_xml.py b/qgis-app/plugins/tasks/generate_plugins_xml.py
index 42493392..b1e60cef 100644
--- a/qgis-app/plugins/tasks/generate_plugins_xml.py
+++ b/qgis-app/plugins/tasks/generate_plugins_xml.py
@@ -1,12 +1,13 @@
import os
+
import requests
from celery import shared_task
-from preferences import preferences
from django.conf import settings
+from preferences import preferences
@shared_task
-def generate_plugins_xml(site = ''):
+def generate_plugins_xml(site=""):
"""
Fetch the xml list of plugins from the plugin site.
:param site: site domain where the plugins will be fetched, default to
@@ -16,39 +17,68 @@ def generate_plugins_xml(site = ''):
if settings.DEFAULT_PLUGINS_SITE:
site = settings.DEFAULT_PLUGINS_SITE
else:
- site = 'http://plugins.qgis.org'
- plugins_url = '{}/plugins/plugins_new.xml'.format(site)
+ site = "http://plugins.qgis.org"
+ plugins_url = "{}/plugins/plugins_new.xml".format(site)
versions = preferences.SitePreference.qgis_versions
if versions:
- versions = versions.split(',')
+ versions = versions.split(",")
else:
versions = [
- '1.8', '2.0', '2.2', '2.4', '2.6', '2.8', '2.10',
- '2.12', '2.14', '2.15', '2.16', '2.17', '2.18',
- '2.99', '3.0', '3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7',
- '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.15', '3.16',
- '3.17', '3.18', '3.19', '3.20', '3.21', '3.22', '3.23', '3.24', '3.25',
+ "1.8",
+ "2.0",
+ "2.2",
+ "2.4",
+ "2.6",
+ "2.8",
+ "2.10",
+ "2.12",
+ "2.14",
+ "2.15",
+ "2.16",
+ "2.17",
+ "2.18",
+ "2.99",
+ "3.0",
+ "3.1",
+ "3.2",
+ "3.3",
+ "3.4",
+ "3.5",
+ "3.6",
+ "3.7",
+ "3.8",
+ "3.9",
+ "3.10",
+ "3.11",
+ "3.12",
+ "3.13",
+ "3.14",
+ "3.15",
+ "3.16",
+ "3.17",
+ "3.18",
+ "3.19",
+ "3.20",
+ "3.21",
+ "3.22",
+ "3.23",
+ "3.24",
+ "3.25",
]
- folder_path = os.path.join(
- settings.MEDIA_ROOT,
- 'cached_xmls'
- )
+ folder_path = os.path.join(settings.MEDIA_ROOT, "cached_xmls")
if not os.path.exists(folder_path):
os.mkdir(folder_path)
for version in versions:
response = requests.get(
- '{url}?qgis={version}'.format(
- url=plugins_url,
- version=version
- )
+ "{url}?qgis={version}".format(url=plugins_url, version=version)
)
if response.status_code == 200:
- file_name = 'plugins_{}.xml'.format(version)
- with open(os.path.join(folder_path, file_name), 'w+') as file:
+ file_name = "plugins_{}.xml".format(version)
+ with open(os.path.join(folder_path, file_name), "w+") as file:
file.write(response.text)
diff --git a/qgis-app/plugins/templates/plugins/user.html b/qgis-app/plugins/templates/plugins/user.html
index ba55b204..0c370502 100644
--- a/qgis-app/plugins/templates/plugins/user.html
+++ b/qgis-app/plugins/templates/plugins/user.html
@@ -27,4 +27,3 @@ {% trans "User Details of: " %} {{ plugin_user.username }}
{{ block.super }}
{% endblock %}
-
diff --git a/qgis-app/plugins/templates/plugins/version_permission_deny.html b/qgis-app/plugins/templates/plugins/version_permission_deny.html
index d5f694fd..81e4c94b 100644
--- a/qgis-app/plugins/templates/plugins/version_permission_deny.html
+++ b/qgis-app/plugins/templates/plugins/version_permission_deny.html
@@ -1,4 +1,4 @@
{% extends 'plugins/plugin_base.html' %}{% load i18n %}
{% block content %}
{% trans "You cannot create or modify versions of this plugin." %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/qgis-app/plugins/templatetags/local_timezone.py b/qgis-app/plugins/templatetags/local_timezone.py
index 1b542b9b..4658b2de 100644
--- a/qgis-app/plugins/templatetags/local_timezone.py
+++ b/qgis-app/plugins/templatetags/local_timezone.py
@@ -9,7 +9,7 @@
def local_timezone(date):
try:
utcdate = date.astimezone(pytz.utc).isoformat()
- result = '%s' % (utcdate, )
+ result = '%s' % (utcdate,)
except AttributeError:
result = date
return mark_safe(result)
diff --git a/qgis-app/plugins/templatetags/plugin_utils.py b/qgis-app/plugins/templatetags/plugin_utils.py
index db634864..22ef9ccb 100755
--- a/qgis-app/plugins/templatetags/plugin_utils.py
+++ b/qgis-app/plugins/templatetags/plugin_utils.py
@@ -1,7 +1,9 @@
from django import template
+
register = template.Library()
-@register.filter('klass')
+
+@register.filter("klass")
def klass(ob):
return ob.__class__.__name__
@@ -9,12 +11,11 @@ def klass(ob):
@register.simple_tag(takes_context=True)
def plugin_title(context):
"""Returns plugin name for title"""
- title = ''
- if 'plugin' in context:
- title = context['plugin'].name
- if 'version' in context:
- title = '{plugin} {version}'.format(
- plugin=context['version'].plugin.name,
- version=context['version'].version
+ title = ""
+ if "plugin" in context:
+ title = context["plugin"].name
+ if "version" in context:
+ title = "{plugin} {version}".format(
+ plugin=context["version"].plugin.name, version=context["version"].version
)
return title
diff --git a/qgis-app/plugins/templatetags/plugins_tagcloud.py b/qgis-app/plugins/templatetags/plugins_tagcloud.py
index 91fb6969..c00c2ae6 100644
--- a/qgis-app/plugins/templatetags/plugins_tagcloud.py
+++ b/qgis-app/plugins/templatetags/plugins_tagcloud.py
@@ -5,50 +5,51 @@
"""
from django import template
+from django.conf import settings as django_settings
+from django.core.exceptions import FieldError
from django.db import models
from django.db.models import Count
-from django.core.exceptions import FieldError
-
-from templatetag_sugar.register import tag
-from templatetag_sugar.parser import Name, Variable, Constant, Optional, Model
-
+from plugins.models import Plugin
from taggit import VERSION as TAGGIT_VERSION
from taggit.managers import TaggableManager
-from taggit.models import TaggedItem, Tag
+from taggit.models import Tag, TaggedItem
from taggit_templatetags import settings
+from templatetag_sugar.parser import Constant, Model, Name, Optional, Variable
+from templatetag_sugar.register import tag
-from plugins.models import Plugin
-
-from django.conf import settings as django_settings
-
-TAGCLOUD_COUNT_GTE = getattr(django_settings, 'TAGCLOUD_COUNT_GTE', None)
-T_MAX = getattr(settings, 'TAGCLOUD_MAX', 6.0)
-T_MIN = getattr(settings, 'TAGCLOUD_MIN', 1.0)
+TAGCLOUD_COUNT_GTE = getattr(django_settings, "TAGCLOUD_COUNT_GTE", None)
+T_MAX = getattr(settings, "TAGCLOUD_MAX", 6.0)
+T_MIN = getattr(settings, "TAGCLOUD_MIN", 1.0)
register = template.Library()
+
def get_queryset():
- applabel = 'plugins'
- model = 'plugin'
+ applabel = "plugins"
+ model = "plugin"
# filter tagged items
queryset = TaggedItem.objects.filter(content_type__app_label=applabel.lower())
- queryset = queryset.filter(content_type__model=model.lower(), object_id__in = Plugin.approved_objects.values_list('id', flat=True))
+ queryset = queryset.filter(
+ content_type__model=model.lower(),
+ object_id__in=Plugin.approved_objects.values_list("id", flat=True),
+ )
# get tags
- tag_ids = queryset.values_list('tag_id', flat=True)
+ tag_ids = queryset.values_list("tag_id", flat=True)
queryset = Tag.objects.filter(id__in=tag_ids)
# Retain compatibility with older versions of Django taggit
# a version check (for example taggit.VERSION <= (0,8,0)) does NOT
# work because of the version (0,8,0) of the current dev version of django-taggit
try:
- qs = queryset.annotate(num_times=Count('taggeditem_items'))
+ qs = queryset.annotate(num_times=Count("taggeditem_items"))
except FieldError:
- qs = queryset.annotate(num_times=Count('taggit_taggeditem_items'))
+ qs = queryset.annotate(num_times=Count("taggit_taggeditem_items"))
if TAGCLOUD_COUNT_GTE:
qs = qs.filter(num_times__gte=TAGCLOUD_COUNT_GTE)
return qs
+
def get_weight_fun(t_min, t_max, f_min, f_max):
def weight_fun(f_i, t_min=t_min, t_max=t_max, f_min=f_min, f_max=f_max):
# Prevent a division by zero here, found to occur under some
@@ -56,38 +57,45 @@ def weight_fun(f_i, t_min=t_min, t_max=t_max, f_min=f_min, f_max=f_max):
if f_max == f_min:
mult_fac = 1.0
else:
- mult_fac = float(t_max-t_min)/float(f_max-f_min)
+ mult_fac = float(t_max - t_min) / float(f_max - f_min)
+
+ return t_max - (f_max - f_i) * mult_fac
- return t_max - (f_max-f_i)*mult_fac
return weight_fun
-@tag(register, [Constant('as'), Name()])
+
+@tag(register, [Constant("as"), Name()])
def get_plugins_taglist(context, asvar):
queryset = get_queryset()
- queryset = queryset.order_by('-num_times')
+ queryset = queryset.order_by("-num_times")
context[asvar] = queryset
- return ''
+ return ""
+
-@tag(register, [Constant('as'), Name()])
+@tag(register, [Constant("as"), Name()])
def get_plugins_tagcloud(context, asvar):
queryset = get_queryset()
- num_times = queryset.values_list('num_times', flat=True)
- if (len(num_times) == 0):
+ num_times = queryset.values_list("num_times", flat=True)
+ if len(num_times) == 0:
context[asvar] = queryset
- return ''
+ return ""
weight_fun = get_weight_fun(T_MIN, T_MAX, min(num_times), max(num_times))
- queryset = queryset.order_by('name')
+ queryset = queryset.order_by("name")
for tag in queryset:
tag.weight = weight_fun(tag.num_times)
context[asvar] = queryset
- return ''
+ return ""
+
def include_plugins_tagcloud(forvar=None):
pass
+
def include_plugins_taglist(forvar=None):
pass
-register.inclusion_tag('plugins/plugins_taglist_include.html')(include_plugins_taglist)
-register.inclusion_tag('plugins/plugins_tagcloud_include.html')(include_plugins_tagcloud)
+register.inclusion_tag("plugins/plugins_taglist_include.html")(include_plugins_taglist)
+register.inclusion_tag("plugins/plugins_tagcloud_include.html")(
+ include_plugins_tagcloud
+)
diff --git a/qgis-app/plugins/templatetags/range_filter.py b/qgis-app/plugins/templatetags/range_filter.py
index 28f7dc3d..c1090b59 100755
--- a/qgis-app/plugins/templatetags/range_filter.py
+++ b/qgis-app/plugins/templatetags/range_filter.py
@@ -2,9 +2,10 @@
register = Library()
+
@register.filter
-def get_range( value ):
- """
+def get_range(value):
+ """
Filter - returns a list containing range made from given value
Usage (in template):
@@ -20,7 +21,7 @@ def get_range( value ):
Instead of 3 one may use the variable set in the views
- """
- if not value:
- value = 0
- return range( value )
+ """
+ if not value:
+ value = 0
+ return range(value)
diff --git a/qgis-app/plugins/templatetags/smart_paginate.py b/qgis-app/plugins/templatetags/smart_paginate.py
index 6ed8c626..9c706a40 100644
--- a/qgis-app/plugins/templatetags/smart_paginate.py
+++ b/qgis-app/plugins/templatetags/smart_paginate.py
@@ -8,17 +8,17 @@
from sets import Set as set
from django import template
-from django.http import Http404
-from django.core.paginator import Paginator, InvalidPage
from django.conf import settings
+from django.core.paginator import InvalidPage, Paginator
+from django.http import Http404
register = template.Library()
-DEFAULT_PAGINATION = getattr(settings, 'PAGINATION_DEFAULT_PAGINATION', 20)
-DEFAULT_WINDOW = getattr(settings, 'PAGINATION_DEFAULT_WINDOW', 4)
-DEFAULT_ORPHANS = getattr(settings, 'PAGINATION_DEFAULT_ORPHANS', 0)
-INVALID_PAGE_RAISES_404 = getattr(settings,
- 'PAGINATION_INVALID_PAGE_RAISES_404', False)
+DEFAULT_PAGINATION = getattr(settings, "PAGINATION_DEFAULT_PAGINATION", 20)
+DEFAULT_WINDOW = getattr(settings, "PAGINATION_DEFAULT_WINDOW", 4)
+DEFAULT_ORPHANS = getattr(settings, "PAGINATION_DEFAULT_ORPHANS", 0)
+INVALID_PAGE_RAISES_404 = getattr(settings, "PAGINATION_INVALID_PAGE_RAISES_404", False)
+
def do_autopaginate(parser, token):
"""
@@ -28,33 +28,39 @@ def do_autopaginate(parser, token):
as_index = None
context_var = None
for i, bit in enumerate(split):
- if bit == 'as':
+ if bit == "as":
as_index = i
break
if as_index is not None:
try:
context_var = split[as_index + 1]
except IndexError:
- raise template.TemplateSyntaxError("Context variable assignment " +
- "must take the form of {%% %r object.example_set.all ... as " +
- "context_var_name %%}" % split[0])
- del split[as_index:as_index + 2]
+ raise template.TemplateSyntaxError(
+ "Context variable assignment "
+ + "must take the form of {%% %r object.example_set.all ... as "
+ + "context_var_name %%}" % split[0]
+ )
+ del split[as_index : as_index + 2]
if len(split) == 2:
return AutoPaginateNode(split[1])
elif len(split) == 3:
- return AutoPaginateNode(split[1], paginate_by=split[2],
- context_var=context_var)
+ return AutoPaginateNode(split[1], paginate_by=split[2], context_var=context_var)
elif len(split) == 4:
try:
orphans = int(split[3])
except ValueError:
- raise template.TemplateSyntaxError(u'Got %s, but expected integer.'
- % split[3])
- return AutoPaginateNode(split[1], paginate_by=split[2], orphans=orphans,
- context_var=context_var)
+ raise template.TemplateSyntaxError(
+ u"Got %s, but expected integer." % split[3]
+ )
+ return AutoPaginateNode(
+ split[1], paginate_by=split[2], orphans=orphans, context_var=context_var
+ )
else:
- raise template.TemplateSyntaxError('%r tag takes one required ' +
- 'argument and one optional argument' % split[0])
+ raise template.TemplateSyntaxError(
+ "%r tag takes one required "
+ + "argument and one optional argument" % split[0]
+ )
+
class AutoPaginateNode(template.Node):
"""
@@ -73,8 +79,14 @@ class AutoPaginateNode(template.Node):
tag. If you choose not to use *{% paginate %}*, make sure to display the
list of available pages, or else the application may seem to be buggy.
"""
- def __init__(self, queryset_var, paginate_by=DEFAULT_PAGINATION,
- orphans=DEFAULT_ORPHANS, context_var=None):
+
+ def __init__(
+ self,
+ queryset_var,
+ paginate_by=DEFAULT_PAGINATION,
+ orphans=DEFAULT_ORPHANS,
+ context_var=None,
+ ):
self.queryset_var = template.Variable(queryset_var)
if isinstance(paginate_by, int):
self.paginate_by = paginate_by
@@ -92,24 +104,26 @@ def render(self, context):
paginate_by = self.paginate_by.resolve(context)
paginator = Paginator(value, paginate_by, self.orphans)
try:
- page_obj = paginator.page(context['request'].page)
+ page_obj = paginator.page(context["request"].page)
except InvalidPage:
if INVALID_PAGE_RAISES_404:
- raise Http404('Invalid page requested. If DEBUG were set to ' +
- 'False, an HTTP 404 page would have been shown instead.')
+ raise Http404(
+ "Invalid page requested. If DEBUG were set to "
+ + "False, an HTTP 404 page would have been shown instead."
+ )
context[key] = []
- context['invalid_page'] = True
- return u''
+ context["invalid_page"] = True
+ return u""
if self.context_var is not None:
context[self.context_var] = page_obj.object_list
else:
context[key] = page_obj.object_list
- context['paginator'] = paginator
- context['page_obj'] = page_obj
- return u''
+ context["paginator"] = paginator
+ context["page_obj"] = page_obj
+ return u""
-def smart_paginate(context, window=DEFAULT_WINDOW, hashtag=''):
+def smart_paginate(context, window=DEFAULT_WINDOW, hashtag=""):
"""
Renders the ``pagination/pagination.html`` template, resulting in a
Digg-like display of the available pages, given the current page. If there
@@ -133,26 +147,26 @@ def smart_paginate(context, window=DEFAULT_WINDOW, hashtag=''):
A dictionary of all of the **GET** parameters in the current request.
This is useful to maintain certain types of state, even when requesting
a different page.
- """
+ """
try:
- paginator = context['paginator']
- page_obj = context['page_obj']
+ paginator = context["paginator"]
+ page_obj = context["page_obj"]
page_range = paginator.page_range
# Calculate the record range in the current page for display.
- records = {'first': 1 + (page_obj.number - 1) * paginator.per_page}
- records['last'] = records['first'] + paginator.per_page - 1
- if records['last'] + paginator.orphans >= paginator.count:
- records['last'] = paginator.count
+ records = {"first": 1 + (page_obj.number - 1) * paginator.per_page}
+ records["last"] = records["first"] + paginator.per_page - 1
+ if records["last"] + paginator.orphans >= paginator.count:
+ records["last"] = paginator.count
# First and last are simply the first *n* pages and the last *n* pages,
# where *n* is the current window size.
first = set(page_range[:window])
last = set(page_range[-window:])
# Now we look around our current page, making sure that we don't wrap
# around.
- current_start = page_obj.number-1-window
+ current_start = page_obj.number - 1 - window
if current_start < 0:
current_start = 0
- current_end = page_obj.number-1+window
+ current_end = page_obj.number - 1 + window
if current_end < 0:
current_end = 0
current = set(page_range[current_start:current_end])
@@ -217,27 +231,27 @@ def smart_paginate(context, window=DEFAULT_WINDOW, hashtag=''):
else:
per_page_list = []
to_return = {
- 'MEDIA_URL': settings.MEDIA_URL,
- 'pages': pages,
- 'records': records,
- 'page_obj': page_obj,
- 'per_page_list' : per_page_list,
- 'paginator': paginator,
- 'hashtag': hashtag,
- 'is_paginated': paginator.count > paginator.per_page,
+ "MEDIA_URL": settings.MEDIA_URL,
+ "pages": pages,
+ "records": records,
+ "page_obj": page_obj,
+ "per_page_list": per_page_list,
+ "paginator": paginator,
+ "hashtag": hashtag,
+ "is_paginated": paginator.count > paginator.per_page,
}
- if 'request' in context:
- getvars = context['request'].GET.copy()
- if 'page' in getvars:
- del getvars['page']
+ if "request" in context:
+ getvars = context["request"].GET.copy()
+ if "page" in getvars:
+ del getvars["page"]
if len(getvars.keys()) > 0:
- to_return['getvars'] = "&%s" % getvars.urlencode()
+ to_return["getvars"] = "&%s" % getvars.urlencode()
else:
- to_return['getvars'] = ''
+ to_return["getvars"] = ""
return to_return
# ABP: turned exceptions into a tuple
except (KeyError, AttributeError):
return {}
-register.inclusion_tag('plugins/pagination.html', takes_context=True)(
- smart_paginate)
+
+register.inclusion_tag("plugins/pagination.html", takes_context=True)(smart_paginate)
diff --git a/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/__init__.py
index 50d32515..0e3e06b7 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.0-spaced/Hello World/__init__.py
@@ -2,17 +2,28 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "Hello World"
+ return "Hello World"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.0"
+ return "Version 1.0"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
diff --git a/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/__init__.py
index 978df2ef..452d0afd 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.0/HelloWorld/__init__.py
@@ -2,21 +2,32 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.0"
+ return "Version 1.0"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
"""
Icon
"""
- return "icon.png"
\ No newline at end of file
+ return "icon.png"
diff --git a/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/__init__.py
index 2b0dafd3..5f7dc3df 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.1/HelloWorld/__init__.py
@@ -2,21 +2,32 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
"""
Icon
"""
- return "icon.png"
\ No newline at end of file
+ return "icon.png"
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/__init__.py
index 8140a7b1..58656605 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/__init__.py
@@ -2,30 +2,48 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
+
+
def icon():
"""
Icon
"""
return "icon.png"
+
+
def deprecated():
return True
+
+
def experimental():
return True
+
def author():
return "Alessandro Secondo"
+
def email():
return "email2@email.com"
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/metadata.txt
index 1e4a8600..65be10da 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-md-full/HelloWorld/metadata.txt
@@ -27,5 +27,3 @@ icon=icon.png
experimental=True
; test numeric value flag
deprecated=1
-
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/__init__.py
index adc39bd6..f70b70c6 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/__init__.py
@@ -2,17 +2,28 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
@@ -25,5 +36,6 @@ def icon():
def author():
return "Alessandro Secondo"
+
def email():
return "email2@email.com"
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/metadata.txt
index 6be3e311..e77b9aeb 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-md-txt-incomplete/HelloWorld/metadata.txt
@@ -1,3 +1,2 @@
[general]
nodata=true
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/__init__.py
index 483c69c0..fe04c5c0 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/__init__.py
@@ -2,15 +2,25 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/metadata.txt
index 8baa2aeb..2aa2d5be 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-no-icon/HelloWorld/metadata.txt
@@ -19,5 +19,3 @@ tags=wkt,raster,hello world
tracker=http://bugs.itopen.it
homepage=http://www.itopen.it
repository=http://www.itopen.it/repo
-
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/__init__.py
index 2b0dafd3..5f7dc3df 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/__init__.py
@@ -2,21 +2,32 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
"""
Icon
"""
- return "icon.png"
\ No newline at end of file
+ return "icon.png"
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/metadata.txt
index 8a1b7bda..2aa2d5be 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-md-init/HelloWorld/metadata.txt
@@ -19,4 +19,3 @@ tags=wkt,raster,hello world
tracker=http://bugs.itopen.it
homepage=http://www.itopen.it
repository=http://www.itopen.it/repo
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/metadata.txt
index 8a1b7bda..2aa2d5be 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-qgs-1.6-no-init/HelloWorld/metadata.txt
@@ -19,4 +19,3 @@ tags=wkt,raster,hello world
tracker=http://bugs.itopen.it
homepage=http://www.itopen.it
repository=http://www.itopen.it/repo
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/__init__.py
index 2b0dafd3..5f7dc3df 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/__init__.py
@@ -2,21 +2,32 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
"""
Icon
"""
- return "icon.png"
\ No newline at end of file
+ return "icon.png"
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/metadata.txt
index f3c50a4a..c13f54ef 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2-wierdname/HelloWorld/metadata.txt
@@ -22,5 +22,3 @@ homepage=http://www.itopen.it
repository=http://www.itopen.it/repo
icon=icon.png
-
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/__init__.py
index 2b0dafd3..5f7dc3df 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/__init__.py
@@ -2,21 +2,32 @@
"""
This script initializes the plugin, making it known to QGIS.
"""
+
+
def name():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def description():
- return "HelloWorld"
+ return "HelloWorld"
+
+
def version():
- return "Version 1.1"
+ return "Version 1.1"
+
+
def qgisMinimumVersion():
- return "1.0"
+ return "1.0"
+
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
def icon():
"""
Icon
"""
- return "icon.png"
\ No newline at end of file
+ return "icon.png"
diff --git a/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/metadata.txt
index 922f8b3b..91b7081d 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.2/HelloWorld/metadata.txt
@@ -21,5 +21,3 @@ homepage=http://www.itopen.it
repository=http://www.itopen.it/repo
icon=icon.png
-
-
diff --git a/qgis-app/plugins/tests/HelloWorld/1.3-full-md-no-init/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.3-full-md-no-init/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.3-full-md-no-init/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.3-full-md-no-init/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/metadata.txt
index e420ac7a..778919e0 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.5-full-md-no-init-experimental/HelloWorld/metadata.txt
@@ -24,7 +24,7 @@ repository=http://www.itopen.it/repo
-experimental=True
+experimental=True
; change icon...
diff --git a/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.6-full-md-no-init-unicode-error/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/metadata.txt
index 7c60d7dc..ad48068a 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.7-full-md-no-init-subfolder-icon/HelloWorld/metadata.txt
@@ -24,7 +24,7 @@ repository=http://www.itopen.it/repo
-experimental=True
+experimental=True
; change icon...
diff --git a/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/metadata.txt
index f17ed3a5..a6bdae93 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.8-author-slashes-error/HelloWorld/metadata.txt
@@ -24,7 +24,7 @@ repository=http://www.itopen.it/repo
-experimental=True
+experimental=True
; change icon...
diff --git a/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/metadata.txt b/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/metadata.txt
index fb214c4d..7d075e12 100644
--- a/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/metadata.txt
+++ b/qgis-app/plugins/tests/HelloWorld/1.9-full-md-max_qgs_version/HelloWorld/metadata.txt
@@ -25,7 +25,7 @@ repository=http://www.itopen.it/repo
-experimental=False
+experimental=False
; change icon...
diff --git a/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.0-full-md-empty-max_qgs_version/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.1-full-md-max_qgs_version_2.999/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.2-full-md-empty-max_qgs_category_web/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/HelloWorld.py b/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/HelloWorld.py
index cd0f69e3..45bdac04 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/HelloWorld.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/HelloWorld.py
@@ -6,7 +6,6 @@
class HelloWorld:
-
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
@@ -14,27 +13,28 @@ def __init__(self, iface):
def initGui(self):
# Create action that will start plugin
- self.action = QAction(QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow())
+ self.action = QAction(
+ QIcon(":/plugins/"), "&HelloWorld", self.iface.mainWindow()
+ )
# connect the action to the run method
QObject.connect(self.action, SIGNAL("activated()"), self.hello_world)
# Add toolbar button and menu item
self.iface.addPluginToMenu("HelloWorld", self.action)
-
def unload(self):
# Remove the plugin menu item and icon
- self.iface.removePluginMenu("HelloWorld",self.action)
-
-
+ self.iface.removePluginMenu("HelloWorld", self.action)
# run
def hello_world(self):
- QMessageBox.information(self.iface.mainWindow(), QCoreApplication.translate('HelloWorld', "HelloWorld"), QCoreApplication.translate('HelloWorld', "HelloWorld"))
+ QMessageBox.information(
+ self.iface.mainWindow(),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ QCoreApplication.translate("HelloWorld", "HelloWorld"),
+ )
return
-
-
if __name__ == "__main__":
pass
diff --git a/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/__init__.py b/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/__init__.py
index 7b224b25..eff9426a 100644
--- a/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/__init__.py
+++ b/qgis-app/plugins/tests/HelloWorld/2.3-full-changed-repository/HelloWorld/__init__.py
@@ -3,6 +3,8 @@
This script initializes the plugin, making it known to QGIS.
"""
+
def classFactory(iface):
- from HelloWorld import HelloWorld
- return HelloWorld(iface)
+ from HelloWorld import HelloWorld
+
+ return HelloWorld(iface)
diff --git a/qgis-app/plugins/tests/__init__.py b/qgis-app/plugins/tests/__init__.py
index 803fccab..53d131f4 100644
--- a/qgis-app/plugins/tests/__init__.py
+++ b/qgis-app/plugins/tests/__init__.py
@@ -1,5 +1,5 @@
from plugins.tests import ws_test
__test__ = {
- 'ws_test': ws_test,
- }
+ "ws_test": ws_test,
+}
diff --git a/qgis-app/plugins/tests/test_filter_template.py b/qgis-app/plugins/tests/test_filter_template.py
index abbbcf9b..8b2419f0 100644
--- a/qgis-app/plugins/tests/test_filter_template.py
+++ b/qgis-app/plugins/tests/test_filter_template.py
@@ -1,14 +1,14 @@
-import pytz
from datetime import datetime
+
+import pytz
from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
-
from plugins.models import Plugin, PluginVersion
class TestPluginFilterTemplate(TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self) -> None:
self.creator = User.objects.create(
@@ -17,18 +17,18 @@ def setUp(self) -> None:
# set creator password to password
self.creator.set_password("password")
self.creator.save()
- self.plugin_name = 'plugin_name_test'
+ self.plugin_name = "plugin_name_test"
self.plugin = Plugin.objects.create(
created_by=self.creator,
name=self.plugin_name,
- package_name=self.plugin_name
+ package_name=self.plugin_name,
)
self.version = PluginVersion.objects.create(
plugin=self.plugin,
created_by=self.creator,
- version='1.1.0',
- min_qg_version='0.0.1',
- max_qg_version='2.2.0'
+ version="1.1.0",
+ min_qg_version="0.0.1",
+ max_qg_version="2.2.0",
)
self.created_on = datetime(2022, 1, 1, 1, 0, 0)
self.version.created_on = self.created_on
@@ -40,8 +40,9 @@ def tearDown(self) -> None:
self.version.delete()
def test_detail_plugin_version_tab_displaying_local_timezone(self):
- url = reverse("plugin_detail", kwargs={
- 'package_name': self.plugin.package_name})
+ url = reverse(
+ "plugin_detail", kwargs={"package_name": self.plugin.package_name}
+ )
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(
@@ -49,6 +50,7 @@ def test_detail_plugin_version_tab_displaying_local_timezone(self):
'{}'.format(
self.created_on.astimezone(pytz.utc).isoformat()
),
- 'utf-8') in
- response.content
+ "utf-8",
+ )
+ in response.content
)
diff --git a/qgis-app/plugins/tests/test_simple_tag.py b/qgis-app/plugins/tests/test_simple_tag.py
index 255effd4..b09d4f5b 100644
--- a/qgis-app/plugins/tests/test_simple_tag.py
+++ b/qgis-app/plugins/tests/test_simple_tag.py
@@ -1,12 +1,11 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
-
from plugins.models import Plugin, PluginVersion
class TestPluginSimpleTag(TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def setUp(self) -> None:
self.creator = User.objects.create(
@@ -15,18 +14,18 @@ def setUp(self) -> None:
# set creator password to password
self.creator.set_password("password")
self.creator.save()
- self.plugin_name = 'plugin_name_test'
+ self.plugin_name = "plugin_name_test"
self.plugin = Plugin.objects.create(
created_by=self.creator,
name=self.plugin_name,
- package_name=self.plugin_name
+ package_name=self.plugin_name,
)
self.version = PluginVersion.objects.create(
plugin=self.plugin,
created_by=self.creator,
- version='1.1.0',
- min_qg_version='0.0.1',
- max_qg_version='2.2.0'
+ version="1.1.0",
+ min_qg_version="0.0.1",
+ max_qg_version="2.2.0",
)
def tearDown(self) -> None:
@@ -35,27 +34,34 @@ def tearDown(self) -> None:
self.version.delete()
def test_return_plugin_name(self):
- url = reverse("plugin_detail", kwargs={
- 'package_name': self.plugin.package_name})
+ url = reverse(
+ "plugin_detail", kwargs={"package_name": self.plugin.package_name}
+ )
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(
- bytes('{} — QGIS Python Plugins Repository'.format(
- self.plugin.name), 'utf-8') in
- response.content
+ bytes(
+ "{} — QGIS Python Plugins Repository".format(self.plugin.name), "utf-8"
+ )
+ in response.content
)
def test_return_plugin_name_in_version_view(self):
- url = reverse("version_detail", kwargs={
- 'package_name': self.plugin.package_name,
- 'version': self.version.version
- })
+ url = reverse(
+ "version_detail",
+ kwargs={
+ "package_name": self.plugin.package_name,
+ "version": self.version.version,
+ },
+ )
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(
- bytes('{plugin} {version} — QGIS Python Plugins Repository'.format(
- plugin=self.plugin.name,
- version=self.version.version
- ), 'utf-8') in
- response.content
+ bytes(
+ "{plugin} {version} — QGIS Python Plugins Repository".format(
+ plugin=self.plugin.name, version=self.version.version
+ ),
+ "utf-8",
+ )
+ in response.content
)
diff --git a/qgis-app/plugins/tests/test_validator.py b/qgis-app/plugins/tests/test_validator.py
index e291dc56..54fe0b2c 100644
--- a/qgis-app/plugins/tests/test_validator.py
+++ b/qgis-app/plugins/tests/test_validator.py
@@ -1,34 +1,27 @@
import os
-import requests
-
from unittest import mock
+import requests
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.test import TestCase
+from plugins.validator import _check_url_link, validator
-
-from plugins.validator import validator, _check_url_link
-
-TESTFILE_DIR = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'testfiles'))
+TESTFILE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "testfiles"))
class TestValidatorMetadataPlugins(TestCase):
-
def setUp(self):
- invalid_plugins = os.path.join(
- TESTFILE_DIR, "invalid_metadata_link.zip")
+ invalid_plugins = os.path.join(TESTFILE_DIR, "invalid_metadata_link.zip")
invalid_url_scheme_plugins = os.path.join(
- TESTFILE_DIR, "invalid_url_scheme.zip")
- web_not_exist_plugins= os.path.join(
- TESTFILE_DIR, "web_not_exist.zip")
- valid_plugins = os.path.join(
- TESTFILE_DIR, "valid_metadata_link.zip")
- self.valid_metadata_link = open(valid_plugins, 'rb')
- self.invalid_metadata_link = open(invalid_plugins, 'rb')
- self.web_not_exist = open(web_not_exist_plugins, 'rb')
- self.invalid_url_scheme = open(invalid_url_scheme_plugins,'rb')
+ TESTFILE_DIR, "invalid_url_scheme.zip"
+ )
+ web_not_exist_plugins = os.path.join(TESTFILE_DIR, "web_not_exist.zip")
+ valid_plugins = os.path.join(TESTFILE_DIR, "valid_metadata_link.zip")
+ self.valid_metadata_link = open(valid_plugins, "rb")
+ self.invalid_metadata_link = open(invalid_plugins, "rb")
+ self.web_not_exist = open(web_not_exist_plugins, "rb")
+ self.invalid_url_scheme = open(invalid_url_scheme_plugins, "rb")
def tearDown(self):
self.valid_metadata_link.close()
@@ -37,16 +30,18 @@ def tearDown(self):
self.web_not_exist.close()
def test_valid_metadata(self):
- self.assertTrue(validator(
- InMemoryUploadedFile(
- self.valid_metadata_link,
- field_name='tempfile',
- name='testfile.zip',
- content_type='application/zip',
- size=39889,
- charset='utf8'
+ self.assertTrue(
+ validator(
+ InMemoryUploadedFile(
+ self.valid_metadata_link,
+ field_name="tempfile",
+ name="testfile.zip",
+ content_type="application/zip",
+ size=39889,
+ charset="utf8",
+ )
)
- ))
+ )
def test_invalid_metadata_link_tracker_repo_homepage(self):
"""
@@ -63,12 +58,12 @@ def test_invalid_metadata_link_tracker_repo_homepage(self):
validator,
InMemoryUploadedFile(
self.invalid_metadata_link,
- field_name='tempfile',
- name='testfile.zip',
- content_type='application/zip',
+ field_name="tempfile",
+ name="testfile.zip",
+ content_type="application/zip",
size=39889,
- charset='utf8'
- )
+ charset="utf8",
+ ),
)
def test_invalid_metadata_url_scheme(self):
@@ -86,12 +81,12 @@ def test_invalid_metadata_url_scheme(self):
validator,
InMemoryUploadedFile(
self.invalid_url_scheme,
- field_name='tempfile',
- name='testfile.zip',
- content_type='application/zip',
+ field_name="tempfile",
+ name="testfile.zip",
+ content_type="application/zip",
size=39889,
- charset='utf8'
- )
+ charset="utf8",
+ ),
)
def test_invalid_metadata_web_does_not_exist(self):
@@ -109,27 +104,25 @@ def test_invalid_metadata_web_does_not_exist(self):
validator,
InMemoryUploadedFile(
self.web_not_exist,
- field_name='tempfile',
- name='testfile.zip',
- content_type='application/zip',
+ field_name="tempfile",
+ name="testfile.zip",
+ content_type="application/zip",
size=39889,
- charset='utf8'
- )
+ charset="utf8",
+ ),
)
- @mock.patch('requests.get', side_effect=requests.exceptions.SSLError())
+ @mock.patch("requests.get", side_effect=requests.exceptions.SSLError())
def test_check_url_link_ssl_error(self, mock_request):
- url = 'http://example.com/'
- self.assertIsNone(
- _check_url_link(url, 'forbidden_url', 'metadata attribute')
- )
+ url = "http://example.com/"
+ self.assertIsNone(_check_url_link(url, "forbidden_url", "metadata attribute"))
- @mock.patch('requests.get', side_effect=requests.exceptions.HTTPError())
+ @mock.patch("requests.get", side_effect=requests.exceptions.HTTPError())
def test_check_url_link_does_not_exist(self, mock_request):
- url = 'http://example.com/'
+ url = "http://example.com/"
self.assertRaises(
ValidationError,
- _check_url_link(url, 'forbidden_url', 'metadata attribute'),
+ _check_url_link(url, "forbidden_url", "metadata attribute"),
)
@@ -137,63 +130,66 @@ class TestValidatorForbiddenFileFolder(TestCase):
"""Test if zipfile is not containing forbidden folders and files """
def setUp(self) -> None:
- valid_plugins = os.path.join(
- TESTFILE_DIR, "valid_metadata_link.zip")
- self.valid_metadata_link = open(valid_plugins, 'rb')
+ valid_plugins = os.path.join(TESTFILE_DIR, "valid_metadata_link.zip")
+ self.valid_metadata_link = open(valid_plugins, "rb")
self.package = InMemoryUploadedFile(
self.valid_metadata_link,
- field_name='tempfile',
- name='testfile.zip',
- content_type='application/zip',
+ field_name="tempfile",
+ name="testfile.zip",
+ content_type="application/zip",
size=1234,
- charset='utf8'
+ charset="utf8",
)
def tearDown(self):
self.valid_metadata_link.close()
- @mock.patch('zipfile.ZipFile.namelist')
+ @mock.patch("zipfile.ZipFile.namelist")
def test_zipfile_with_pyc_file(self, mock_namelist):
- mock_namelist.return_value = ['.pyc']
+ mock_namelist.return_value = [".pyc"]
with self.assertRaisesMessage(
- Exception,
- 'For security reasons, zip file cannot contain .pyc file'):
+ Exception, "For security reasons, zip file cannot contain .pyc file"
+ ):
validator(self.package)
- @mock.patch('zipfile.ZipFile.namelist')
+ @mock.patch("zipfile.ZipFile.namelist")
def test_zipfile_with_MACOSX(self, mock_namelist):
- mock_namelist.return_value = ['__MACOSX/']
+ mock_namelist.return_value = ["__MACOSX/"]
with self.assertRaisesMessage(
- Exception,
- ("For security reasons, zip file cannot contain "
- "'__MACOSX' directory")):
+ Exception,
+ ("For security reasons, zip file cannot contain " "'__MACOSX' directory"),
+ ):
validator(self.package)
- @mock.patch('zipfile.ZipFile.namelist')
+ @mock.patch("zipfile.ZipFile.namelist")
def test_zipfile_with_pycache(self, mock_namelist):
- mock_namelist.return_value = ['__pycache__/']
+ mock_namelist.return_value = ["__pycache__/"]
with self.assertRaisesMessage(
- Exception,
- ("For security reasons, zip file cannot contain "
- "'__pycache__' directory")):
+ Exception,
+ (
+ "For security reasons, zip file cannot contain "
+ "'__pycache__' directory"
+ ),
+ ):
validator(self.package)
- @mock.patch('zipfile.ZipFile.namelist')
+ @mock.patch("zipfile.ZipFile.namelist")
def test_zipfile_with_git(self, mock_namelist):
- mock_namelist.return_value = ['.git']
+ mock_namelist.return_value = [".git"]
with self.assertRaisesMessage(
- Exception,
- ("For security reasons, zip file cannot contain "
- "'.git' directory")):
+ Exception,
+ ("For security reasons, zip file cannot contain " "'.git' directory"),
+ ):
validator(self.package)
- @mock.patch('zipfile.ZipFile.namelist')
+ @mock.patch("zipfile.ZipFile.namelist")
def test_zipfile_with_gitignore(self, mock_namelist):
"""test if .gitignore will not raise ValidationError"""
- mock_namelist.return_value = ['.gitignore']
+ mock_namelist.return_value = [".gitignore"]
with self.assertRaises(ValidationError) as cm:
validator(self.package)
exception = cm.exception
self.assertNotEqual(
exception.message,
- "For security reasons, zip file cannot contain '.git' directory")
+ "For security reasons, zip file cannot contain '.git' directory",
+ )
diff --git a/qgis-app/plugins/tests/tests.py b/qgis-app/plugins/tests/tests.py
index 2247054b..3748f41b 100644
--- a/qgis-app/plugins/tests/tests.py
+++ b/qgis-app/plugins/tests/tests.py
@@ -7,6 +7,7 @@
from django.test import TestCase
+
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
@@ -14,10 +15,12 @@ def test_basic_addition(self):
"""
self.failUnlessEqual(1 + 1, 2)
-__test__ = {"doctest": """
+
+__test__ = {
+ "doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
-"""}
-
+"""
+}
diff --git a/qgis-app/plugins/tests/upload_test.py b/qgis-app/plugins/tests/upload_test.py
index 32d3036b..0c05cc97 100644
--- a/qgis-app/plugins/tests/upload_test.py
+++ b/qgis-app/plugins/tests/upload_test.py
@@ -1,6 +1,6 @@
from xmlrpc import client
-server = client.ServerProxy('http://admin:admin@localhost:80/plugins/RPC2/')
+
+server = client.ServerProxy("http://admin:admin@localhost:80/plugins/RPC2/")
handle = open("HelloWorld.zip", "rb")
blob = client.Binary(handle.read())
server.plugin.upload(blob)
-
diff --git a/qgis-app/plugins/tests/versionfield.py b/qgis-app/plugins/tests/versionfield.py
index 8df3b45e..8eff890b 100755
--- a/qgis-app/plugins/tests/versionfield.py
+++ b/qgis-app/plugins/tests/versionfield.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-#-*- coding:utf-8 -*-
+# -*- coding:utf-8 -*-
"""
Tests for version comparison field
@@ -15,30 +15,29 @@
import re
-
-VERSION_RE = r'(^|(?<=\.))0+(?!\.)|\.#+'
+VERSION_RE = r"(^|(?<=\.))0+(?!\.)|\.#+"
TEST_CASES = (
- '1.0.0',
- '1.0.1',
- '0.0.0',
- '1.0',
- '1.10',
- '1.2',
- '1.9',
- '1.0.a',
- 'a.0.a',
- 'b.a.c',
- 'a.b',
- '0.a.0.1',
- '1.0.rc1',
- '1.1a',
- '1.1b',
- '1.9.0',
+ "1.0.0",
+ "1.0.1",
+ "0.0.0",
+ "1.0",
+ "1.10",
+ "1.2",
+ "1.9",
+ "1.0.a",
+ "a.0.a",
+ "b.a.c",
+ "a.b",
+ "0.a.0.1",
+ "1.0.rc1",
+ "1.1a",
+ "1.1b",
+ "1.9.0",
)
-def vjust(str, level=4, delim='.', bitsize=4, fillchar=' ', force_zero=False):
+def vjust(str, level=4, delim=".", bitsize=4, fillchar=" ", force_zero=False):
"""
Normalize a dotted version string.
@@ -58,13 +57,13 @@ def vjust(str, level=4, delim='.', bitsize=4, fillchar=' ', force_zero=False):
nb = str.count(delim)
if nb < level:
if force_zero:
- str += (level-nb) * (delim+'0')
+ str += (level - nb) * (delim + "0")
else:
- str += (level-nb) * delim
+ str += (level - nb) * delim
parts = []
- for v in str.split(delim)[:level+1]:
+ for v in str.split(delim)[: level + 1]:
if not v:
- parts.append(v.rjust(bitsize, '#'))
+ parts.append(v.rjust(bitsize, "#"))
else:
parts.append(v.rjust(bitsize, fillchar))
return delim.join(parts)
@@ -73,18 +72,18 @@ def vjust(str, level=4, delim='.', bitsize=4, fillchar=' ', force_zero=False):
def test():
transformed = []
for v in TEST_CASES:
- vj = vjust(v, level=5, fillchar='0')
+ vj = vjust(v, level=5, fillchar="0")
transformed.append(vj)
- ck = re.sub(VERSION_RE, '', vj)
- print ("Testing\t %s (%s)\t\t %s" % (v, ck, vj))
+ ck = re.sub(VERSION_RE, "", vj)
+ print("Testing\t %s (%s)\t\t %s" % (v, ck, vj))
if v != ck:
- print ("!!! failed !!!")
+ print("!!! failed !!!")
# Test sorting
transformed.sort()
- print ("Sorted:")
+ print("Sorted:")
for v in transformed:
- print (v)
+ print(v)
if __name__ == "__main__":
diff --git a/qgis-app/plugins/urls.py b/qgis-app/plugins/urls.py
index 6d0e35bc..50c6c05e 100644
--- a/qgis-app/plugins/urls.py
+++ b/qgis-app/plugins/urls.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
-from django.conf.urls import include, url
-from plugins.models import Plugin, PluginVersion
-from django.utils.translation import ugettext_lazy as _
+from django.conf.urls import include, url
from django.contrib.auth.decorators import login_required, user_passes_test
+from django.utils.translation import ugettext_lazy as _
from plugins.models import Plugin, PluginVersion
from plugins.views import *
from rpc4django.views import serve_rpc_request
@@ -10,87 +9,279 @@
# Plugins filtered views (need user parameter from request)
urlpatterns = [
# XML
- url(r'^plugins_new.xml$', xml_plugins_new, {}, name='xml_plugins_new'),
- url(r'^plugins.xml$', xml_plugins, {}, name='xml_plugins'),
- url(r'^plugins_(?P\d+\.\d+).xml$', xml_plugins, {}, name='xml_plugins_version_filtered_cached'),
- url(r'^version_filtered/(?P\d+\.\d+).xml$', xml_plugins, {}, name='xml_plugins_version_filtered_uncached'),
- url(r'^tags/(?P[^\/]+)/$', TagsPluginsList.as_view(), name='tags_plugins'),
- url(r'^add/$', plugin_upload, {}, name='plugin_upload'),
- url(r'^user/(?P\w+)/block/$', user_block, {}, name='user_block'),
- url(r'^user/(?P\w+)/unblock/$', user_unblock, {}, name='user_unblock'),
- url(r'^user/(?P\w+)/trust/$', user_trust, {}, name='user_trust'),
- url(r'^user/(?P\w+)/untrust/$', user_untrust, {}, name='user_untrust'),
-
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/manage/$', plugin_manage, {}, name='plugin_manage'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/delete/$', plugin_delete, {}, name='plugin_delete'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/update/$', plugin_update, {}, name='plugin_update'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/set_featured/$', plugin_set_featured, {}, name='plugin_set_featured'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/unset_featured/$', plugin_unset_featured, {}, name='plugin_unset_featured'),
-
- url(r'^user/(?P\w+)/admin$', UserDetailsPluginsList.as_view(), name='user_details'),
- url(r'^$', PluginsList.as_view(), name='approved_plugins'),
- url(r'^my$', login_required(MyPluginsList.as_view(additional_context={'title':_('My Plugins')})), name='my_plugins'),
- url(r'^featured/$', PluginsList.as_view(queryset=Plugin.featured_objects.all(), additional_context={'title' : _('Featured plugins')}), name='featured_plugins'),
- url(r'^user/(?P\w+)/$', UserPluginsList.as_view(), name='user_plugins'),
-
-
- url(r'^server/$', PluginsList.as_view(queryset=Plugin.server_objects.all(), additional_context={'title' : _('QGIS Server plugins')}), name='server_plugins'),
- url(r'^unapproved/$', PluginsList.as_view(queryset=Plugin.unapproved_objects.all(), additional_context={'title' : _('Unapproved plugins')}), name='unapproved_plugins'),
- url(r'^deprecated/$', PluginsList.as_view(queryset=Plugin.deprecated_objects.all(), additional_context={'title' : _('Deprecated plugins')}), name='deprecated_plugins'),
- url(r'^fresh/$', PluginsList.as_view(queryset=Plugin.fresh_objects.all(), additional_context={'title' : _('New plugins')}), name='fresh_plugins'),
- url(r'^latest/$', PluginsList.as_view(queryset=Plugin.latest_objects.all(), additional_context={'title' : _('Updated plugins')}), name='latest_plugins'),
- url(r'^stable/$', PluginsList.as_view(queryset=Plugin.stable_objects.all(), additional_context={'title' : _('Stable plugins')}), name='stable_plugins'),
- url(r'^experimental/$', PluginsList.as_view(queryset=Plugin.experimental_objects.all(), additional_context={'title' : _('Experimental plugins')}), name='experimental_plugins'),
- url(r'^popular/$', PluginsList.as_view(queryset=Plugin.popular_objects.all(), additional_context={'title' : _('Popular plugins')}), name='popular_plugins'),
- url(r'^most_voted/$', PluginsList.as_view(queryset=Plugin.most_voted_objects.all(), additional_context={'title' : _('Most voted plugins')}), name='most_voted_plugins'),
- url(r'^most_downloaded/$', PluginsList.as_view(queryset=Plugin.most_downloaded_objects.all(), additional_context={'title' : _('Most downloaded plugins')}), name='most_downloaded_plugins'),
- url(r'^most_voted/$', PluginsList.as_view(queryset=Plugin.most_voted_objects.all(), additional_context={'title' : _('Most voted plugins')}), name='most_voted_plugins'),
- url(r'^most_rated/$', PluginsList.as_view(queryset=Plugin.most_rated_objects.all(), additional_context={'title' : _('Most rated plugins')}), name='most_rated_plugins'),
- url(r'^author/(?P[^/]+)/$', AuthorPluginsList.as_view(), name='author_plugins'),
+ url(r"^plugins_new.xml$", xml_plugins_new, {}, name="xml_plugins_new"),
+ url(r"^plugins.xml$", xml_plugins, {}, name="xml_plugins"),
+ url(
+ r"^plugins_(?P\d+\.\d+).xml$",
+ xml_plugins,
+ {},
+ name="xml_plugins_version_filtered_cached",
+ ),
+ url(
+ r"^version_filtered/(?P\d+\.\d+).xml$",
+ xml_plugins,
+ {},
+ name="xml_plugins_version_filtered_uncached",
+ ),
+ url(r"^tags/(?P[^\/]+)/$", TagsPluginsList.as_view(), name="tags_plugins"),
+ url(r"^add/$", plugin_upload, {}, name="plugin_upload"),
+ url(r"^user/(?P\w+)/block/$", user_block, {}, name="user_block"),
+ url(r"^user/(?P\w+)/unblock/$", user_unblock, {}, name="user_unblock"),
+ url(r"^user/(?P\w+)/trust/$", user_trust, {}, name="user_trust"),
+ url(r"^user/(?P\w+)/untrust/$", user_untrust, {}, name="user_untrust"),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/manage/$",
+ plugin_manage,
+ {},
+ name="plugin_manage",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/delete/$",
+ plugin_delete,
+ {},
+ name="plugin_delete",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/update/$",
+ plugin_update,
+ {},
+ name="plugin_update",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/set_featured/$",
+ plugin_set_featured,
+ {},
+ name="plugin_set_featured",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/unset_featured/$",
+ plugin_unset_featured,
+ {},
+ name="plugin_unset_featured",
+ ),
+ url(
+ r"^user/(?P\w+)/admin$",
+ UserDetailsPluginsList.as_view(),
+ name="user_details",
+ ),
+ url(r"^$", PluginsList.as_view(), name="approved_plugins"),
+ url(
+ r"^my$",
+ login_required(
+ MyPluginsList.as_view(additional_context={"title": _("My Plugins")})
+ ),
+ name="my_plugins",
+ ),
+ url(
+ r"^featured/$",
+ PluginsList.as_view(
+ queryset=Plugin.featured_objects.all(),
+ additional_context={"title": _("Featured plugins")},
+ ),
+ name="featured_plugins",
+ ),
+ url(r"^user/(?P\w+)/$", UserPluginsList.as_view(), name="user_plugins"),
+ url(
+ r"^server/$",
+ PluginsList.as_view(
+ queryset=Plugin.server_objects.all(),
+ additional_context={"title": _("QGIS Server plugins")},
+ ),
+ name="server_plugins",
+ ),
+ url(
+ r"^unapproved/$",
+ PluginsList.as_view(
+ queryset=Plugin.unapproved_objects.all(),
+ additional_context={"title": _("Unapproved plugins")},
+ ),
+ name="unapproved_plugins",
+ ),
+ url(
+ r"^deprecated/$",
+ PluginsList.as_view(
+ queryset=Plugin.deprecated_objects.all(),
+ additional_context={"title": _("Deprecated plugins")},
+ ),
+ name="deprecated_plugins",
+ ),
+ url(
+ r"^fresh/$",
+ PluginsList.as_view(
+ queryset=Plugin.fresh_objects.all(),
+ additional_context={"title": _("New plugins")},
+ ),
+ name="fresh_plugins",
+ ),
+ url(
+ r"^latest/$",
+ PluginsList.as_view(
+ queryset=Plugin.latest_objects.all(),
+ additional_context={"title": _("Updated plugins")},
+ ),
+ name="latest_plugins",
+ ),
+ url(
+ r"^stable/$",
+ PluginsList.as_view(
+ queryset=Plugin.stable_objects.all(),
+ additional_context={"title": _("Stable plugins")},
+ ),
+ name="stable_plugins",
+ ),
+ url(
+ r"^experimental/$",
+ PluginsList.as_view(
+ queryset=Plugin.experimental_objects.all(),
+ additional_context={"title": _("Experimental plugins")},
+ ),
+ name="experimental_plugins",
+ ),
+ url(
+ r"^popular/$",
+ PluginsList.as_view(
+ queryset=Plugin.popular_objects.all(),
+ additional_context={"title": _("Popular plugins")},
+ ),
+ name="popular_plugins",
+ ),
+ url(
+ r"^most_voted/$",
+ PluginsList.as_view(
+ queryset=Plugin.most_voted_objects.all(),
+ additional_context={"title": _("Most voted plugins")},
+ ),
+ name="most_voted_plugins",
+ ),
+ url(
+ r"^most_downloaded/$",
+ PluginsList.as_view(
+ queryset=Plugin.most_downloaded_objects.all(),
+ additional_context={"title": _("Most downloaded plugins")},
+ ),
+ name="most_downloaded_plugins",
+ ),
+ url(
+ r"^most_voted/$",
+ PluginsList.as_view(
+ queryset=Plugin.most_voted_objects.all(),
+ additional_context={"title": _("Most voted plugins")},
+ ),
+ name="most_voted_plugins",
+ ),
+ url(
+ r"^most_rated/$",
+ PluginsList.as_view(
+ queryset=Plugin.most_rated_objects.all(),
+ additional_context={"title": _("Most rated plugins")},
+ ),
+ name="most_rated_plugins",
+ ),
+ url(
+ r"^author/(?P[^/]+)/$",
+ AuthorPluginsList.as_view(),
+ name="author_plugins",
+ ),
]
# User management
urlpatterns += [
- url(r'^user/(?P\w+)/manage/$', user_permissions_manage, {}, name='user_permissions_manage'),
+ url(
+ r"^user/(?P\w+)/manage/$",
+ user_permissions_manage,
+ {},
+ name="user_permissions_manage",
+ ),
]
# Version Management
urlpatterns += [
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/manage/$', version_manage, {}, name='version_manage'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/add/$', version_create, {}, name='version_create'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/$', version_detail, {}, name='version_detail'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/delete/$', version_delete, {}, name='version_delete'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/update/$', version_update, {}, name='version_update'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/download/$', version_download, {}, name='version_download'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/approve/$', version_approve, {}, name='version_approve'),
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/unapprove/$', version_unapprove, {}, name='version_unapprove'),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/manage/$",
+ version_manage,
+ {},
+ name="version_manage",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/add/$",
+ version_create,
+ {},
+ name="version_create",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/$",
+ version_detail,
+ {},
+ name="version_detail",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/delete/$",
+ version_delete,
+ {},
+ name="version_delete",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/update/$",
+ version_update,
+ {},
+ name="version_update",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/download/$",
+ version_download,
+ {},
+ name="version_download",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/approve/$",
+ version_approve,
+ {},
+ name="version_approve",
+ ),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/version/(?P[^\/]+)/unapprove/$",
+ version_unapprove,
+ {},
+ name="version_unapprove",
+ ),
]
# RPC
urlpatterns += [
# rpc4django will need to be in your Python path
- url(r'^RPC2/$', serve_rpc_request),
+ url(r"^RPC2/$", serve_rpc_request),
]
-# plugin rating
-from djangoratings.views import AddRatingFromModel
-from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie
+from django.views.decorators.http import require_POST
+# plugin rating
+from djangoratings.views import AddRatingFromModel
urlpatterns += [
- url(r'rate/(?P\d+)/(?P\d+)/', require_POST(csrf_protect(AddRatingFromModel())), {
- 'app_label': 'plugins',
- 'model': 'plugin',
- 'field_name': 'rating',
- }, name='plugin_rate'),
+ url(
+ r"rate/(?P\d+)/(?P\d+)/",
+ require_POST(csrf_protect(AddRatingFromModel())),
+ {
+ "app_label": "plugins",
+ "model": "plugin",
+ "field_name": "rating",
+ },
+ name="plugin_rate",
+ ),
]
# Plugin detail (keep last)
urlpatterns += [
- url(r'^(?P[A-Za-z][A-Za-z0-9-_]+)/$', PluginDetailView.as_view(slug_url_kwarg='package_name', slug_field='package_name'), name='plugin_detail'),
+ url(
+ r"^(?P[A-Za-z][A-Za-z0-9-_]+)/$",
+ PluginDetailView.as_view(
+ slug_url_kwarg="package_name", slug_field="package_name"
+ ),
+ name="plugin_detail",
+ ),
]
diff --git a/qgis-app/plugins/validator.py b/qgis-app/plugins/validator.py
index 58590fd8..f8079687 100644
--- a/qgis-app/plugins/validator.py
+++ b/qgis-app/plugins/validator.py
@@ -2,26 +2,55 @@
Plugin validator class
"""
-import zipfile
+import codecs
+import configparser
import mimetypes
-import re
import os
-import configparser
+import re
+import zipfile
from io import StringIO
-import codecs
-import requests
from urllib.parse import urlparse
+
+import requests
from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-from django.forms import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile
+from django.forms import ValidationError
+from django.utils.translation import ugettext_lazy as _
-
-PLUGIN_MAX_UPLOAD_SIZE=getattr(settings, 'PLUGIN_MAX_UPLOAD_SIZE', 25000000) # 25 mb
-PLUGIN_REQUIRED_METADATA=getattr(settings, 'PLUGIN_REQUIRED_METADATA', ('name', 'description', 'version', 'qgisMinimumVersion', 'author', 'email', 'about', 'tracker', 'repository'))
-
-PLUGIN_OPTIONAL_METADATA=getattr(settings, 'PLUGIN_OPTIONAL_METADATA', ( 'homepage', 'changelog', 'qgisMaximumVersion', 'tags', 'deprecated', 'experimental', 'external_deps', 'server'))
-PLUGIN_BOOLEAN_METADATA=getattr(settings, 'PLUGIN_BOOLEAN_METADATA', ('experimental', 'deprecated', 'server'))
+PLUGIN_MAX_UPLOAD_SIZE = getattr(settings, "PLUGIN_MAX_UPLOAD_SIZE", 25000000) # 25 mb
+PLUGIN_REQUIRED_METADATA = getattr(
+ settings,
+ "PLUGIN_REQUIRED_METADATA",
+ (
+ "name",
+ "description",
+ "version",
+ "qgisMinimumVersion",
+ "author",
+ "email",
+ "about",
+ "tracker",
+ "repository",
+ ),
+)
+
+PLUGIN_OPTIONAL_METADATA = getattr(
+ settings,
+ "PLUGIN_OPTIONAL_METADATA",
+ (
+ "homepage",
+ "changelog",
+ "qgisMaximumVersion",
+ "tags",
+ "deprecated",
+ "experimental",
+ "external_deps",
+ "server",
+ ),
+)
+PLUGIN_BOOLEAN_METADATA = getattr(
+ settings, "PLUGIN_BOOLEAN_METADATA", ("experimental", "deprecated", "server")
+)
def _read_from_init(initcontent, initname):
@@ -29,29 +58,42 @@ def _read_from_init(initcontent, initname):
Read metadata from __init__.py, raise ValidationError
"""
metadata = []
- i=0
- lines=initcontent.split('\n')
+ i = 0
+ lines = initcontent.split("\n")
while i < len(lines):
- if re.search('def\s+([^\(]+)', lines[i]):
- k=re.search('def\s+([^\(]+)', lines[i]).groups()[0]
- i+=1
- while i < len(lines) and lines[i] != '':
- if re.search('return\s+["\']?([^"\']+)["\']?', lines[i]):
- metadata.append((k, re.search('return\s+["\']?([^"\']+)["\']?', lines[i]).groups()[0]))
+ if re.search("def\s+([^\(]+)", lines[i]):
+ k = re.search("def\s+([^\(]+)", lines[i]).groups()[0]
+ i += 1
+ while i < len(lines) and lines[i] != "":
+ if re.search("return\s+[\"']?([^\"']+)[\"']?", lines[i]):
+ metadata.append(
+ (
+ k,
+ re.search(
+ "return\s+[\"']?([^\"']+)[\"']?", lines[i]
+ ).groups()[0],
+ )
+ )
break
- i+=1
- i+=1
+ i += 1
+ i += 1
if not len(metadata):
- raise ValidationError(_('Cannot find valid metadata in %s') % initname)
+ raise ValidationError(_("Cannot find valid metadata in %s") % initname)
return metadata
+
def _check_required_metadata(metadata):
"""
Checks if required metadata are in place, raise ValidationError if not found
"""
for md in PLUGIN_REQUIRED_METADATA:
if md not in dict(metadata) or not dict(metadata)[md]:
- raise ValidationError(_('Cannot find metadata %s in metadata source %s
.
For further informations about metadata, please see: metadata documentation') % (md, dict(metadata).get('metadata_source')))
+ raise ValidationError(
+ _(
+ 'Cannot find metadata %s in metadata source %s
.
For further informations about metadata, please see: metadata documentation'
+ )
+ % (md, dict(metadata).get("metadata_source"))
+ )
def _check_url_link(url: str, forbidden_url: str, metadata_attr: str) -> None:
@@ -59,10 +101,15 @@ def _check_url_link(url: str, forbidden_url: str, metadata_attr: str) -> None:
Checks if the url link is valid.
"""
error_check = ValidationError(
- _("Please provide valid url link for %s in metadata.") % metadata_attr)
+ _("Please provide valid url link for %s in metadata.") % metadata_attr
+ )
error_check_if_exist = ValidationError(
- _("Please provide valid url link for %s in metadata. "
- "This website cannot be reached.") % metadata_attr)
+ _(
+ "Please provide valid url link for %s in metadata. "
+ "This website cannot be reached."
+ )
+ % metadata_attr
+ )
# check against forbidden_url
is_forbidden_url = url == forbidden_url
@@ -73,8 +120,9 @@ def _check_url_link(url: str, forbidden_url: str, metadata_attr: str) -> None:
# https://stackoverflow.com/a/38020041
try:
parsed_url = urlparse(url) # e.g https://plugins.qgis.org/
- if not (all([parsed_url.scheme, # e.g http
- parsed_url.netloc])): # e.g www.qgis.org
+ if not (
+ all([parsed_url.scheme, parsed_url.netloc]) # e.g http
+ ): # e.g www.qgis.org
raise error_check
except Exception:
raise error_check
@@ -85,9 +133,9 @@ def _check_url_link(url: str, forbidden_url: str, metadata_attr: str) -> None:
# add the headers parameter to make the request appears like coming
# from browser, otherwise some websites will return 403
headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) '
- 'AppleWebKit/537.36 (KHTML, like Gecko) '
- 'Chrome/56.0.2924.76 Safari/537.36'
+ "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/56.0.2924.76 Safari/537.36"
}
req = requests.head(url, headers=headers)
except requests.exceptions.SSLError:
@@ -114,48 +162,75 @@ def validator(package):
"""
try:
if package.size > PLUGIN_MAX_UPLOAD_SIZE:
- raise ValidationError( _("File is too big. Max size is %s Megabytes") % ( PLUGIN_MAX_UPLOAD_SIZE / 1000000 ) )
+ raise ValidationError(
+ _("File is too big. Max size is %s Megabytes")
+ % (PLUGIN_MAX_UPLOAD_SIZE / 1000000)
+ )
except AttributeError:
- if package.len > PLUGIN_MAX_UPLOAD_SIZE:
- raise ValidationError( _("File is too big. Max size is %s Megabytes") % ( PLUGIN_MAX_UPLOAD_SIZE / 1000000 ) )
+ if package.len > PLUGIN_MAX_UPLOAD_SIZE:
+ raise ValidationError(
+ _("File is too big. Max size is %s Megabytes")
+ % (PLUGIN_MAX_UPLOAD_SIZE / 1000000)
+ )
try:
- zip = zipfile.ZipFile( package )
+ zip = zipfile.ZipFile(package)
except:
- raise ValidationError( _("Could not unzip file.") )
+ raise ValidationError(_("Could not unzip file."))
for zname in zip.namelist():
- if zname.find('..') != -1 or zname.find(os.path.sep) == 0 :
- raise ValidationError( _("For security reasons, zip file cannot contain path informations") )
- if zname.find('.pyc') != -1:
- raise ValidationError( _("For security reasons, zip file cannot contain .pyc file") )
- for forbidden_dir in ['__MACOSX', '.git', '__pycache__']:
- if forbidden_dir in zname.split('/'):
- raise ValidationError(_("For security reasons, zip file "
- "cannot contain '%s' directory"
- % (forbidden_dir,)) )
+ if zname.find("..") != -1 or zname.find(os.path.sep) == 0:
+ raise ValidationError(
+ _("For security reasons, zip file cannot contain path informations")
+ )
+ if zname.find(".pyc") != -1:
+ raise ValidationError(
+ _("For security reasons, zip file cannot contain .pyc file")
+ )
+ for forbidden_dir in ["__MACOSX", ".git", "__pycache__"]:
+ if forbidden_dir in zname.split("/"):
+ raise ValidationError(
+ _(
+ "For security reasons, zip file "
+ "cannot contain '%s' directory" % (forbidden_dir,)
+ )
+ )
bad_file = zip.testzip()
if bad_file:
zip.close()
del zip
try:
- raise ValidationError( _('Bad zip (maybe a CRC error) on file %s') % bad_file )
+ raise ValidationError(
+ _("Bad zip (maybe a CRC error) on file %s") % bad_file
+ )
except UnicodeDecodeError:
- raise ValidationError( _('Bad zip (maybe unicode filename) on file %s') % bad_file, errors='replace')
+ raise ValidationError(
+ _("Bad zip (maybe unicode filename) on file %s") % bad_file,
+ errors="replace",
+ )
# Checks that package_name exists
namelist = zip.namelist()
try:
- package_name = namelist[0][:namelist[0].index('/')]
+ package_name = namelist[0][: namelist[0].index("/")]
except:
- raise ValidationError( _("Cannot find a folder inside the compressed package: this does not seems a valid plugin") )
+ raise ValidationError(
+ _(
+ "Cannot find a folder inside the compressed package: this does not seems a valid plugin"
+ )
+ )
# Cuts the trailing slash
- if package_name.endswith('/'):
+ if package_name.endswith("/"):
package_name = package_name[:-1]
- initname = package_name + '/__init__.py'
- metadataname = package_name + '/metadata.txt'
+ initname = package_name + "/__init__.py"
+ metadataname = package_name + "/metadata.txt"
if initname not in namelist and metadataname not in namelist:
- raise ValidationError(_('Cannot find __init__.py or metadata.txt in the compressed package: this does not seems a valid plugin (I searched for %s and %s)') % (initname, metadataname))
+ raise ValidationError(
+ _(
+ "Cannot find __init__.py or metadata.txt in the compressed package: this does not seems a valid plugin (I searched for %s and %s)"
+ )
+ % (initname, metadataname)
+ )
# Checks for __init__.py presence
if initname not in namelist:
@@ -169,87 +244,107 @@ def validator(package):
parser = configparser.ConfigParser()
parser.optionxform = str
parser.readfp(StringIO(codecs.decode(zip.read(metadataname), "utf8")))
- if not parser.has_section('general'):
- raise ValidationError(_("Cannot find a section named 'general' in %s") % metadataname)
- metadata.extend(parser.items('general'))
+ if not parser.has_section("general"):
+ raise ValidationError(
+ _("Cannot find a section named 'general' in %s") % metadataname
+ )
+ metadata.extend(parser.items("general"))
except Exception as e:
raise ValidationError(_("Errors parsing %s. %s") % (metadataname, e))
- metadata.append(('metadata_source', 'metadata.txt'))
+ metadata.append(("metadata_source", "metadata.txt"))
else:
# Then parse __init__
# Ugly RE: regexp guru wanted!
- initcontent = zip.read(initname).decode('utf8')
+ initcontent = zip.read(initname).decode("utf8")
metadata.extend(_read_from_init(initcontent, initname))
if not metadata:
- raise ValidationError(_('Cannot find valid metadata in %s') % initname)
- metadata.append(('metadata_source', '__init__.py'))
+ raise ValidationError(_("Cannot find valid metadata in %s") % initname)
+ metadata.append(("metadata_source", "__init__.py"))
_check_required_metadata(metadata)
# Process Icon
try:
# Strip leading dir for ccrook plugins
- if dict(metadata)['icon'].startswith('./'):
- icon_path = dict(metadata)['icon'][2:]
+ if dict(metadata)["icon"].startswith("./"):
+ icon_path = dict(metadata)["icon"][2:]
else:
- icon_path = dict(metadata)['icon']
- icon = zip.read(package_name + '/' + icon_path)
- icon_file = SimpleUploadedFile(dict(metadata)['icon'], icon, mimetypes.guess_type(dict(metadata)['icon']))
+ icon_path = dict(metadata)["icon"]
+ icon = zip.read(package_name + "/" + icon_path)
+ icon_file = SimpleUploadedFile(
+ dict(metadata)["icon"], icon, mimetypes.guess_type(dict(metadata)["icon"])
+ )
except:
icon_file = None
- metadata.append(('icon_file', icon_file))
+ metadata.append(("icon_file", icon_file))
# Transforms booleans flags (experimental)
for flag in PLUGIN_BOOLEAN_METADATA:
if flag in dict(metadata):
- metadata[metadata.index((flag, dict(metadata)[flag]))] = (flag, dict(metadata)[flag].lower() == 'true' or dict(metadata)[flag].lower() == '1')
+ metadata[metadata.index((flag, dict(metadata)[flag]))] = (
+ flag,
+ dict(metadata)[flag].lower() == "true"
+ or dict(metadata)[flag].lower() == "1",
+ )
# Adds package_name
- if not re.match(r'^[A-Za-z][A-Za-z0-9-_]+$', package_name):
- raise ValidationError(_("The name of the top level directory inside the zip package must start with an ASCII letter and can only contain ASCII letters, digits and the signs '-' and '_'."))
- metadata.append(('package_name', package_name))
+ if not re.match(r"^[A-Za-z][A-Za-z0-9-_]+$", package_name):
+ raise ValidationError(
+ _(
+ "The name of the top level directory inside the zip package must start with an ASCII letter and can only contain ASCII letters, digits and the signs '-' and '_'."
+ )
+ )
+ metadata.append(("package_name", package_name))
# Last temporary rule, check if mandatory metadata are also in __init__.py
# fails if it is not
- min_qgs_version = dict(metadata).get('qgisMinimumVersion')
- max_qgs_version = dict(metadata).get('qgisMaximumVersion')
- if tuple(min_qgs_version.split('.')) < tuple('1.8'.split('.')) and metadataname in namelist:
- initcontent = zip.read(initname).decode('utf8')
+ min_qgs_version = dict(metadata).get("qgisMinimumVersion")
+ dict(metadata).get("qgisMaximumVersion")
+ if (
+ tuple(min_qgs_version.split(".")) < tuple("1.8".split("."))
+ and metadataname in namelist
+ ):
+ initcontent = zip.read(initname).decode("utf8")
try:
initmetadata = _read_from_init(initcontent, initname)
- initmetadata.append(('metadata_source', '__init__.py'))
+ initmetadata.append(("metadata_source", "__init__.py"))
_check_required_metadata(initmetadata)
except ValidationError as e:
- raise ValidationError(_("qgisMinimumVersion is set to less than 1.8 (%s) and there were errors reading metadata from the __init__.py file. This can lead to errors in versions of QGIS less than 1.8, please either set the qgisMinimumVersion to 1.8 or specify the metadata also in the __init__.py file. Reported error was: %s") % (min_qgs_version, ','.join(e.messages)))
+ raise ValidationError(
+ _(
+ "qgisMinimumVersion is set to less than 1.8 (%s) and there were errors reading metadata from the __init__.py file. This can lead to errors in versions of QGIS less than 1.8, please either set the qgisMinimumVersion to 1.8 or specify the metadata also in the __init__.py file. Reported error was: %s"
+ )
+ % (min_qgs_version, ",".join(e.messages))
+ )
# check url_link
- _check_url_link(
- dict(metadata).get('tracker'), 'http://bugs', 'Bug tracker')
- _check_url_link(
- dict(metadata).get('repository'), 'http://repo', 'Repository'
- )
- _check_url_link(
- dict(metadata).get('homepage'), 'http://homepage', 'Home page'
- )
+ _check_url_link(dict(metadata).get("tracker"), "http://bugs", "Bug tracker")
+ _check_url_link(dict(metadata).get("repository"), "http://repo", "Repository")
+ _check_url_link(dict(metadata).get("homepage"), "http://homepage", "Home page")
zip.close()
del zip
# Check author
- if 'author' in dict(metadata):
- if not re.match(r'^[^/]+$', dict(metadata)['author']):
- raise ValidationError(_("Author name cannot contain slashes."))
+ if "author" in dict(metadata):
+ if not re.match(r"^[^/]+$", dict(metadata)["author"]):
+ raise ValidationError(_("Author name cannot contain slashes."))
# strip and check
checked_metadata = []
- for k,v in metadata:
+ for k, v in metadata:
try:
- if not (k in PLUGIN_BOOLEAN_METADATA or k == 'icon_file'):
- #v.decode('UTF-8')
+ if not (k in PLUGIN_BOOLEAN_METADATA or k == "icon_file"):
+ # v.decode('UTF-8')
checked_metadata.append((k, v.strip()))
else:
checked_metadata.append((k, v))
except UnicodeDecodeError as e:
- raise ValidationError(_("There was an error converting metadata '%s' to UTF-8 . Reported error was: %s") % (k, e))
+ raise ValidationError(
+ _(
+ "There was an error converting metadata '%s' to UTF-8 . Reported error was: %s"
+ )
+ % (k, e)
+ )
return checked_metadata
diff --git a/qgis-app/plugins/views.py b/qgis-app/plugins/views.py
index d625e2fd..1c65043b 100644
--- a/qgis-app/plugins/views.py
+++ b/qgis-app/plugins/views.py
@@ -1,82 +1,86 @@
# Create your views here.
+import copy
+import logging
import os
-from django.db import IntegrityError
-from django.db import connection
-from django.db.models import Q, Max
-from django.db.models.functions import Lower
-from django.db.models.expressions import RawSQL
-from django.shortcuts import get_object_or_404, render
-from django.http import Http404, HttpResponse, HttpResponseRedirect
+
+from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required, user_passes_test
-from django.urls import reverse
-from django.core.exceptions import FieldDoesNotExist
-from django.conf import settings
-from django.contrib.auth.models import User, Permission
+from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
-from django.utils.encoding import DjangoUnicodeDecodeError
+from django.contrib.sites.models import Site
+from django.core.exceptions import FieldDoesNotExist
+from django.core.mail import send_mail
+from django.db import IntegrityError, connection
+from django.db.models import Max, Q
+from django.db.models.expressions import RawSQL
+from django.db.models.functions import Lower
+from django.http import Http404, HttpResponse, HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
+from django.urls import reverse
from django.utils.decorators import method_decorator
+from django.utils.encoding import DjangoUnicodeDecodeError
from django.utils.translation import ugettext_lazy as _
-#from sortable_listview import SortableListView
-from django.views.generic.list import ListView
+from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie
+from django.views.decorators.http import require_POST
from django.views.generic.detail import DetailView
-from plugins.models import Plugin, PluginVersion, vjust
+
+# from sortable_listview import SortableListView
+from django.views.generic.list import ListView
from plugins.forms import *
+from plugins.models import Plugin, PluginVersion, vjust
from plugins.validator import PLUGIN_REQUIRED_METADATA
-from django.views.decorators.http import require_POST
-from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie
-
-
-from django.core.mail import send_mail
-from django.contrib.sites.models import Site
-import logging
-import copy
-
try:
- from urllib import urlencode, unquote
- from urlparse import urlparse, parse_qs
+ from urllib import unquote, urlencode
+
+ from urlparse import parse_qs, urlparse
except ImportError:
- from urllib.parse import urlencode, urlparse, parse_qs, unquote
+ from urllib.parse import parse_qs, unquote, urlencode, urlparse
# Decorator
staff_required = user_passes_test(lambda u: u.is_staff)
from plugins.tasks.generate_plugins_xml import generate_plugins_xml
-def send_mail_wrapper(subject,
- message,
- mail_from,
- recipients,
- fail_silently=True):
+def send_mail_wrapper(subject, message, mail_from, recipients, fail_silently=True):
if settings.DEBUG:
logging.debug("Mail not sent (DEBUG=True)")
else:
- send_mail(subject,
- message,
- mail_from,
- recipients,
- fail_silently)
+ send_mail(subject, message, mail_from, recipients, fail_silently)
+
def plugin_notify(plugin):
"""
Sends a message to staff on new plugins
"""
- recipients = [u.email for u in User.objects.filter(is_staff=True, email__isnull=False).exclude(email='')]
+ recipients = [
+ u.email
+ for u in User.objects.filter(is_staff=True, email__isnull=False).exclude(
+ email=""
+ )
+ ]
if recipients:
- domain = Site.objects.get_current().domain
- mail_from = settings.DEFAULT_FROM_EMAIL
-
- send_mail_wrapper(
- _('A new plugin has been created by %s.') % plugin.created_by,
- _('\r\nPlugin name is: %s\r\nPlugin description is: %s\r\nLink: http://%s%s\r\n') % (plugin.name, plugin.description, domain, plugin.get_absolute_url()),
- mail_from,
- recipients,
- fail_silently=True)
- logging.debug('Sending email notification for %s plugin, recipients: %s' % (plugin, recipients))
+ domain = Site.objects.get_current().domain
+ mail_from = settings.DEFAULT_FROM_EMAIL
+
+ send_mail_wrapper(
+ _("A new plugin has been created by %s.") % plugin.created_by,
+ _(
+ "\r\nPlugin name is: %s\r\nPlugin description is: %s\r\nLink: http://%s%s\r\n"
+ )
+ % (plugin.name, plugin.description, domain, plugin.get_absolute_url()),
+ mail_from,
+ recipients,
+ fail_silently=True,
+ )
+ logging.debug(
+ "Sending email notification for %s plugin, recipients: %s"
+ % (plugin, recipients)
+ )
else:
- logging.warning('No recipients found for %s plugin notification' % plugin)
+ logging.warning("No recipients found for %s plugin notification" % plugin)
def version_notify(plugin_version):
@@ -85,22 +89,40 @@ def version_notify(plugin_version):
"""
plugin = plugin_version.plugin
- recipients = [u.email for u in User.objects.filter(is_staff=True, email__isnull=False).exclude(email='')]
+ recipients = [
+ u.email
+ for u in User.objects.filter(is_staff=True, email__isnull=False).exclude(
+ email=""
+ )
+ ]
if recipients:
domain = Site.objects.get_current().domain
mail_from = settings.DEFAULT_FROM_EMAIL
send_mail_wrapper(
- _('A new plugin version has been uploaded by %s.') % plugin.created_by,
- _('\r\nPlugin name is: %s\r\nPlugin description is: %s\r\nLink: http://%s%s\r\n') % (plugin.name, plugin.description, domain, plugin_version.get_absolute_url()),
+ _("A new plugin version has been uploaded by %s.") % plugin.created_by,
+ _(
+ "\r\nPlugin name is: %s\r\nPlugin description is: %s\r\nLink: http://%s%s\r\n"
+ )
+ % (
+ plugin.name,
+ plugin.description,
+ domain,
+ plugin_version.get_absolute_url(),
+ ),
mail_from,
recipients,
- fail_silently=True)
- logging.debug('Sending email notification for %s plugin version, recipients: %s' % (plugin_version, recipients))
+ fail_silently=True,
+ )
+ logging.debug(
+ "Sending email notification for %s plugin version, recipients: %s"
+ % (plugin_version, recipients)
+ )
else:
- logging.warning('No recipients found for %s plugin version notification' % plugin_version)
-
+ logging.warning(
+ "No recipients found for %s plugin version notification" % plugin_version
+ )
def plugin_approve_notify(plugin, msg, user):
@@ -113,24 +135,37 @@ def plugin_approve_notify(plugin, msg, user):
if settings.QGIS_DEV_MAILING_LIST_ADDRESS:
recipients.append(settings.QGIS_DEV_MAILING_LIST_ADDRESS)
if plugin.approved:
- approval_state = 'approval'
- approved_state = 'approved'
+ approval_state = "approval"
else:
- approval_state = 'unapproval'
- approved_state = 'unapproved'
+ approval_state = "unapproval"
if len(recipients):
domain = Site.objects.get_current().domain
mail_from = settings.DEFAULT_FROM_EMAIL
- logging.debug('Sending email %s notification for %s plugin, recipients: %s' % (approval_state, plugin, recipients))
+ logging.debug(
+ "Sending email %s notification for %s plugin, recipients: %s"
+ % (approval_state, plugin, recipients)
+ )
send_mail_wrapper(
- _('Plugin %s %s notification.') % (plugin, approval_state),
- _('\r\nPlugin %s %s by %s.\r\n%s\r\nLink: http://%s%s\r\n') % (plugin.name, approval_state, user, msg, domain, plugin.get_absolute_url()),
- mail_from,
- recipients,
- fail_silently=True)
+ _("Plugin %s %s notification.") % (plugin, approval_state),
+ _("\r\nPlugin %s %s by %s.\r\n%s\r\nLink: http://%s%s\r\n")
+ % (
+ plugin.name,
+ approval_state,
+ user,
+ msg,
+ domain,
+ plugin.get_absolute_url(),
+ ),
+ mail_from,
+ recipients,
+ fail_silently=True,
+ )
else:
- logging.warning('No recipients found for %s plugin %s notification' % (plugin, approval_state))
+ logging.warning(
+ "No recipients found for %s plugin %s notification"
+ % (plugin, approval_state)
+ )
def user_trust_notify(user):
@@ -140,31 +175,34 @@ def user_trust_notify(user):
if settings.DEBUG:
return
if user.is_staff:
- logging.debug('Skipping trust notification for staff user %s' % user)
+ logging.debug("Skipping trust notification for staff user %s" % user)
else:
if user.email:
recipients = [user.email]
mail_from = settings.DEFAULT_FROM_EMAIL
- if user.has_perm('plugins.can_approve'):
- subject = _('User trust notification.')
- message = _('\r\nYou can now approve your own plugins and the plugins you can edit.\r\n')
+ if user.has_perm("plugins.can_approve"):
+ subject = _("User trust notification.")
+ message = _(
+ "\r\nYou can now approve your own plugins and the plugins you can edit.\r\n"
+ )
else:
- subject = _('User untrust notification.')
- message = _('\r\nYou cannot approve any plugin.\r\n')
+ subject = _("User untrust notification.")
+ message = _("\r\nYou cannot approve any plugin.\r\n")
- logging.debug('Sending email trust change notification to %s' % recipients)
+ logging.debug("Sending email trust change notification to %s" % recipients)
send_mail_wrapper(
- subject,
- message,
- mail_from,
- recipients,
- fail_silently=True)
+ subject, message, mail_from, recipients, fail_silently=True
+ )
else:
- logging.warning('No email found for %s user trust change notification' % user)
+ logging.warning(
+ "No email found for %s user trust change notification" % user
+ )
+
## Access control ##
+
def check_plugin_access(user, plugin):
"""
Returns true if the user can modify the plugin:
@@ -184,7 +222,9 @@ def check_plugin_version_approval_rights(user, plugin):
* is owner and is trusted
"""
- return user.is_staff or (user in plugin.editors and user.has_perm('plugins.can_approve'))
+ return user.is_staff or (
+ user in plugin.editors and user.has_perm("plugins.can_approve")
+ )
@login_required
@@ -194,11 +234,13 @@ def plugin_create(request):
There is a more "automatic" alternative for creating new Plugins in a single step
through package upload
"""
- if request.method == 'POST':
+ if request.method == "POST":
form = PluginForm(request.POST, request.FILES)
- form.fields['owners'].queryset = User.objects.exclude(pk=request.user.pk).order_by('username')
+ form.fields["owners"].queryset = User.objects.exclude(
+ pk=request.user.pk
+ ).order_by("username")
if form.is_valid():
- plugin = form.save(commit = False)
+ plugin = form.save(commit=False)
plugin.created_by = request.user
plugin.save()
plugin_notify(plugin)
@@ -207,10 +249,15 @@ def plugin_create(request):
return HttpResponseRedirect(plugin.get_absolute_url())
else:
form = PluginForm()
- form.fields['owners'].queryset = User.objects.exclude(pk=request.user.pk).order_by('username')
-
- return render(request, 'plugins/plugin_form.html', { 'form' : form , 'form_title' : _('New plugin')})
+ form.fields["owners"].queryset = User.objects.exclude(
+ pk=request.user.pk
+ ).order_by("username")
+ return render(
+ request,
+ "plugins/plugin_form.html",
+ {"form": form, "form_title": _("New plugin")},
+ )
@staff_required
@@ -248,63 +295,82 @@ def plugin_upload(request):
uploads a package and creates a new Plugin with a new PluginVersion
can also update an existing plugin
"""
- if request.method == 'POST':
+ if request.method == "POST":
form = PackageUploadForm(request.POST, request.FILES)
if form.is_valid():
try:
plugin_data = {
- 'name' : form.cleaned_data['name'],
- 'package_name' : form.cleaned_data['package_name'],
- 'description' : form.cleaned_data['description'],
- 'created_by' : request.user,
- 'author' : form.cleaned_data['author'],
- 'email' : form.cleaned_data['email'],
- 'created_by' : request.user,
- 'icon' : form.cleaned_data['icon_file'],
+ "name": form.cleaned_data["name"],
+ "package_name": form.cleaned_data["package_name"],
+ "description": form.cleaned_data["description"],
+ "created_by": request.user,
+ "author": form.cleaned_data["author"],
+ "email": form.cleaned_data["email"],
+ "created_by": request.user,
+ "icon": form.cleaned_data["icon_file"],
}
# Gets existing plugin
try:
- plugin = Plugin.objects.get(package_name=plugin_data['package_name'])
+ plugin = Plugin.objects.get(
+ package_name=plugin_data["package_name"]
+ )
if not check_plugin_access(request.user, plugin):
- return render(request, 'plugins/plugin_permission_deny.html', {})
+ return render(
+ request, "plugins/plugin_permission_deny.html", {}
+ )
# Apply new values
- plugin.name = plugin_data['name']
- plugin.description = plugin_data['description']
- plugin.author = plugin_data['author']
- plugin.email = plugin_data['email']
+ plugin.name = plugin_data["name"]
+ plugin.description = plugin_data["description"]
+ plugin.author = plugin_data["author"]
+ plugin.email = plugin_data["email"]
is_new = False
except Plugin.DoesNotExist:
plugin = Plugin(**plugin_data)
is_new = True
# Check icon, don't change if not valid
- if plugin_data['icon']:
- plugin.icon = plugin_data['icon']
+ if plugin_data["icon"]:
+ plugin.icon = plugin_data["icon"]
# Server is optional
- plugin.server = form.cleaned_data.get('server', False)
+ plugin.server = form.cleaned_data.get("server", False)
# Other optional fields
warnings = []
- if form.cleaned_data.get('homepage'):
- plugin.homepage = form.cleaned_data.get('homepage')
+ if form.cleaned_data.get("homepage"):
+ plugin.homepage = form.cleaned_data.get("homepage")
elif not plugin.homepage:
- warnings.append(_('homepage field is empty, this field is not required but is recommended, please consider adding it to metadata.'))
- if form.cleaned_data.get('tracker'):
- plugin.tracker = form.cleaned_data.get('tracker')
+ warnings.append(
+ _(
+ "homepage field is empty, this field is not required but is recommended, please consider adding it to metadata."
+ )
+ )
+ if form.cleaned_data.get("tracker"):
+ plugin.tracker = form.cleaned_data.get("tracker")
elif not plugin.tracker:
- raise ValidationError(_('"tracker" metadata is required! Please add it to metadata.txt
.'))
- if form.cleaned_data.get('repository'):
- plugin.repository = form.cleaned_data.get('repository')
+ raise ValidationError(
+ _(
+ '"tracker" metadata is required! Please add it to metadata.txt
.'
+ )
+ )
+ if form.cleaned_data.get("repository"):
+ plugin.repository = form.cleaned_data.get("repository")
elif not plugin.repository:
- raise ValidationError(_('"repository" metadata is required! Please add it to metadata.txt
.'))
- if form.cleaned_data.get('about'):
- plugin.about = form.cleaned_data.get('about')
+ raise ValidationError(
+ _(
+ '"repository" metadata is required! Please add it to metadata.txt
.'
+ )
+ )
+ if form.cleaned_data.get("about"):
+ plugin.about = form.cleaned_data.get("about")
elif not plugin.about:
- raise ValidationError(_('"about" metadata is required! Please add it to metadata.txt
.'))
-
+ raise ValidationError(
+ _(
+ '"about" metadata is required! Please add it to metadata.txt
.'
+ )
+ )
# Save main Plugin object
plugin.save()
@@ -313,20 +379,26 @@ def plugin_upload(request):
plugin_notify(plugin)
# Takes care of tags
- if form.cleaned_data.get('tags'):
- plugin.tags.set(*[t.strip().lower() for t in form.cleaned_data.get('tags').split(',')])
-
- version_data = {
- 'plugin' : plugin,
- 'min_qg_version' : form.cleaned_data.get('qgisMinimumVersion'),
- 'max_qg_version' : form.cleaned_data.get('qgisMaximumVersion'),
- 'version' : form.cleaned_data.get('version'),
- 'created_by' : request.user,
- 'package' : form.cleaned_data.get('package'),
- 'approved' : request.user.has_perm('plugins.can_approve') or plugin.approved,
- 'experimental' : form.cleaned_data.get('experimental'),
- 'changelog' : form.cleaned_data.get('changelog', ''),
- 'external_deps' : form.cleaned_data.get('external_deps', ''),
+ if form.cleaned_data.get("tags"):
+ plugin.tags.set(
+ *[
+ t.strip().lower()
+ for t in form.cleaned_data.get("tags").split(",")
+ ]
+ )
+
+ version_data = {
+ "plugin": plugin,
+ "min_qg_version": form.cleaned_data.get("qgisMinimumVersion"),
+ "max_qg_version": form.cleaned_data.get("qgisMaximumVersion"),
+ "version": form.cleaned_data.get("version"),
+ "created_by": request.user,
+ "package": form.cleaned_data.get("package"),
+ "approved": request.user.has_perm("plugins.can_approve")
+ or plugin.approved,
+ "experimental": form.cleaned_data.get("experimental"),
+ "changelog": form.cleaned_data.get("changelog", ""),
+ "external_deps": form.cleaned_data.get("external_deps", ""),
}
new_version = PluginVersion(**version_data)
@@ -338,30 +410,37 @@ def plugin_upload(request):
generate_plugins_xml.delay()
if not new_version.approved:
- msg = _("Your plugin is awaiting approval from a staff member and will be approved as soon as possible.")
+ msg = _(
+ "Your plugin is awaiting approval from a staff member and will be approved as soon as possible."
+ )
warnings.append(msg)
if not is_new:
version_notify(new_version)
- if not form.cleaned_data.get('metadata_source') == 'metadata.txt':
- msg = _("Your plugin does not contain a metadata.txt file, metadata have been read from the __init__.py file. This is deprecated and its support will eventually cease.")
+ if not form.cleaned_data.get("metadata_source") == "metadata.txt":
+ msg = _(
+ "Your plugin does not contain a metadata.txt file, metadata have been read from the __init__.py file. This is deprecated and its support will eventually cease."
+ )
warnings.append(msg)
# Grouped messages:
if warnings:
- messages.warning(request, _('Warnings:
') + '\n'.join([("%s
" % w) for w in warnings]), fail_silently=True)
-
+ messages.warning(
+ request,
+ _("Warnings:
")
+ + "\n".join([("%s
" % w) for w in warnings]),
+ fail_silently=True,
+ )
except (IntegrityError, ValidationError, DjangoUnicodeDecodeError) as e:
connection.close()
messages.error(request, e, fail_silently=True)
if not plugin.pk:
- return render(request, 'plugins/plugin_upload.html', { 'form' : form })
+ return render(request, "plugins/plugin_upload.html", {"form": form})
return HttpResponseRedirect(plugin.get_absolute_url())
else:
form = PackageUploadForm()
- return render(request, 'plugins/plugin_upload.html', { 'form' : form })
-
+ return render(request, "plugins/plugin_upload.html", {"form": form})
class PluginDetailView(DetailView):
@@ -373,21 +452,29 @@ def dispatch(self, *args, **kwargs):
return super(PluginDetailView, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
- plugin = kwargs.get('object')
+ plugin = kwargs.get("object")
context = super(PluginDetailView, self).get_context_data(**kwargs)
# Warnings for owners
if check_plugin_access(self.request.user, plugin):
if not plugin.homepage:
- msg = _('homepage metadata is missing, this is not required but recommended. Please consider adding "homepage" to metadata.txt
.')
+ msg = _(
+ 'homepage metadata is missing, this is not required but recommended. Please consider adding "homepage" to metadata.txt
.'
+ )
messages.warning(self.request, msg, fail_silently=True)
- for md in set(PLUGIN_REQUIRED_METADATA) - set(('version', 'qgisMinimumVersion')):
+ for md in set(PLUGIN_REQUIRED_METADATA) - set(
+ ("version", "qgisMinimumVersion")
+ ):
if not getattr(plugin, md, None):
- msg = _('%s metadata is missing, this metadata entry is required. Please add %s to metadata.txt
.')% (md, md)
+ msg = _(
+ "%s metadata is missing, this metadata entry is required. Please add %s to metadata.txt
."
+ ) % (md, md)
messages.error(self.request, msg, fail_silently=True)
- context.update({
- 'rating': int(plugin.rating.get_rating()),
- 'votes': plugin.rating.votes,
- })
+ context.update(
+ {
+ "rating": int(plugin.rating.get_rating()),
+ "votes": plugin.rating.votes,
+ }
+ )
return context
@@ -395,21 +482,27 @@ def get_context_data(self, **kwargs):
def plugin_delete(request, package_name):
plugin = get_object_or_404(Plugin, package_name=package_name)
if not check_plugin_access(request.user, plugin):
- return render(request, 'plugins/plugin_permission_deny.html', {})
- if 'delete_confirm' in request.POST:
+ return render(request, "plugins/plugin_permission_deny.html", {})
+ if "delete_confirm" in request.POST:
plugin.delete()
msg = _("The Plugin has been successfully deleted.")
messages.success(request, msg, fail_silently=True)
- return HttpResponseRedirect(reverse('approved_plugins'))
- return render(request, 'plugins/plugin_delete_confirm.html', { 'plugin' : plugin })
+ return HttpResponseRedirect(reverse("approved_plugins"))
+ return render(request, "plugins/plugin_delete_confirm.html", {"plugin": plugin})
def _check_optional_metadata(form, request):
"""
Checks for the presence of optional metadata
"""
- if not form.cleaned_data.get('homepage'):
- messages.warning(request, _('Homepage field is empty, this field is not required but is recommended, please consider adding it to metadata.txt
.'), fail_silently=True)
+ if not form.cleaned_data.get("homepage"):
+ messages.warning(
+ request,
+ _(
+ "Homepage field is empty, this field is not required but is recommended, please consider adding it to metadata.txt
."
+ ),
+ fail_silently=True,
+ )
@login_required
@@ -419,10 +512,12 @@ def plugin_update(request, package_name):
"""
plugin = get_object_or_404(Plugin, package_name=package_name)
if not check_plugin_access(request.user, plugin):
- return render(request, 'plugins/plugin_permission_deny.html', {})
- if request.method == 'POST':
+ return render(request, "plugins/plugin_permission_deny.html", {})
+ if request.method == "POST":
form = PluginForm(request.POST, request.FILES, instance=plugin)
- form.fields['owners'].queryset = User.objects.exclude(pk=plugin.created_by.pk).order_by('username')
+ form.fields["owners"].queryset = User.objects.exclude(
+ pk=plugin.created_by.pk
+ ).order_by("username")
if form.is_valid():
new_object = form.save(commit=False)
new_object.modified_by = request.user
@@ -430,7 +525,7 @@ def plugin_update(request, package_name):
# Without this next line the tags won't be saved.
form.save_m2m()
new_object.owners.clear()
- for o in form.cleaned_data['owners']:
+ for o in form.cleaned_data["owners"]:
new_object.owners.add(o)
msg = _("The Plugin has been successfully updated.")
messages.success(request, msg, fail_silently=True)
@@ -440,17 +535,22 @@ def plugin_update(request, package_name):
return HttpResponseRedirect(new_object.get_absolute_url())
else:
- form = PluginForm(instance = plugin)
- form.fields['owners'].queryset = User.objects.exclude(pk=plugin.created_by.pk).order_by('username')
-
- return render(request, 'plugins/plugin_form.html', { 'form' : form , 'form_title' : _('Edit plugin'), 'plugin' : plugin})
+ form = PluginForm(instance=plugin)
+ form.fields["owners"].queryset = User.objects.exclude(
+ pk=plugin.created_by.pk
+ ).order_by("username")
+ return render(
+ request,
+ "plugins/plugin_form.html",
+ {"form": form, "form_title": _("Edit plugin"), "plugin": plugin},
+ )
class PluginsList(ListView):
model = Plugin
queryset = Plugin.approved_objects.all()
- title = _('All plugins')
+ title = _("All plugins")
additional_context = {}
paginate_by = settings.PAGINATION_DEFAULT_PAGINATION
@@ -459,7 +559,7 @@ def get_paginate_by(self, queryset):
Paginate by specified value in querystring, or use default class property value.
"""
try:
- paginate_by = int(self.request.GET.get('per_page', self.paginate_by))
+ paginate_by = int(self.request.GET.get("per_page", self.paginate_by))
except ValueError:
paginate_by = self.paginate_by
return paginate_by
@@ -470,9 +570,9 @@ def get_filtered_queryset(self, qs):
def get_queryset(self):
qs = super(PluginsList, self).get_queryset()
qs = self.get_filtered_queryset(qs)
- sort_by = self.request.GET.get('sort', None)
+ sort_by = self.request.GET.get("sort", None)
if sort_by:
- if sort_by[0] == '-':
+ if sort_by[0] == "-":
_sort_by = sort_by[1:]
else:
_sort_by = sort_by
@@ -480,41 +580,43 @@ def get_queryset(self):
# Check if the sort criterion is a field or 'average_vote'
# or 'latest_version_date'
try:
- (_sort_by == 'average_vote'
- or _sort_by == 'latest_version_date'
- or self.model._meta.get_field(_sort_by))
+ (
+ _sort_by == "average_vote"
+ or _sort_by == "latest_version_date"
+ or self.model._meta.get_field(_sort_by)
+ )
except FieldDoesNotExist:
return qs
qs = qs.order_by(sort_by)
else:
# default
if not qs.ordered:
- qs = qs.order_by(Lower('name'))
+ qs = qs.order_by(Lower("name"))
return qs
def get_context_data(self, **kwargs):
context = super(PluginsList, self).get_context_data(**kwargs)
- context.update({
- 'title': self.title,
- })
+ context.update(
+ {
+ "title": self.title,
+ }
+ )
context.update(self.additional_context)
- context['current_sort_query'] = self.get_sortstring()
- context['current_querystring'] = self.get_querystring()
+ context["current_sort_query"] = self.get_sortstring()
+ context["current_querystring"] = self.get_querystring()
return context
-
def get_sortstring(self):
- if self.request.GET.get('sort', None):
- return 'sort=%s' % self.request.GET.get('sort')
- return ''
-
+ if self.request.GET.get("sort", None):
+ return "sort=%s" % self.request.GET.get("sort")
+ return ""
def get_querystring(self):
"""
Clean existing query string (GET parameters) by removing
arguments that we don't want to preserve (sort parameter, 'page')
"""
- to_remove = ['page', 'sort']
+ to_remove = ["page", "sort"]
query_string = urlparse(self.request.get_full_path()).query
query_dict = parse_qs(query_string)
for arg in to_remove:
@@ -525,66 +627,69 @@ def get_querystring(self):
class MyPluginsList(PluginsList):
-
def get_filtered_queryset(self, qs):
- return Plugin.base_objects.filter(owners=self.request.user).distinct()\
- | Plugin.objects.filter(created_by=self.request.user).distinct()
+ return (
+ Plugin.base_objects.filter(owners=self.request.user).distinct()
+ | Plugin.objects.filter(created_by=self.request.user).distinct()
+ )
class UserPluginsList(PluginsList):
-
def get_filtered_queryset(self, qs):
- user = get_object_or_404(User, username=self.kwargs['username'])
+ user = get_object_or_404(User, username=self.kwargs["username"])
return qs.filter(created_by=user)
class AuthorPluginsList(PluginsList):
-
def get_filtered_queryset(self, qs):
- return qs.filter(author=unquote(self.kwargs['author']))
+ return qs.filter(author=unquote(self.kwargs["author"]))
def get_context_data(self, **kwargs):
context = super(AuthorPluginsList, self).get_context_data(**kwargs)
- context.update({
- 'title' : _('Plugins by %s') % unquote(self.kwargs['author']),
- })
+ context.update(
+ {
+ "title": _("Plugins by %s") % unquote(self.kwargs["author"]),
+ }
+ )
return context
-
class UserDetailsPluginsList(PluginsList):
"""
List plugins created_by OR owned by user
"""
- template_name = 'plugins/user.html'
+
+ template_name = "plugins/user.html"
+
def get_filtered_queryset(self, qs):
- user = get_object_or_404(User, username=self.kwargs['username'])
+ user = get_object_or_404(User, username=self.kwargs["username"])
return qs.filter(Q(created_by=user) | Q(owners=user))
def get_context_data(self, **kwargs):
- user = get_object_or_404(User, username=self.kwargs['username'])
- user_is_trusted = user.has_perm('plugins.can_approve')
+ user = get_object_or_404(User, username=self.kwargs["username"])
+ user_is_trusted = user.has_perm("plugins.can_approve")
context = super(UserDetailsPluginsList, self).get_context_data(**kwargs)
- context.update({
- 'title' : _('Plugins from %s') % user,
- 'user_is_trusted' : user_is_trusted,
- 'plugin_user': user,
- })
+ context.update(
+ {
+ "title": _("Plugins from %s") % user,
+ "user_is_trusted": user_is_trusted,
+ "plugin_user": user,
+ }
+ )
return context
class TagsPluginsList(PluginsList):
-
def get_filtered_queryset(self, qs):
- return (
- qs.filter(tagged_items__tag__slug=unquote(self.kwargs['tags']))
- )
+ return qs.filter(tagged_items__tag__slug=unquote(self.kwargs["tags"]))
def get_context_data(self, **kwargs):
context = super(TagsPluginsList, self).get_context_data(**kwargs)
- context.update({
- 'title' : _('Plugins tagged with: %s') % unquote(self.kwargs['tags']),
- })
+ context.update(
+ {
+ "title": _("Plugins tagged with: %s") % unquote(self.kwargs["tags"]),
+ }
+ )
return context
@@ -594,15 +699,14 @@ def plugin_manage(request, package_name):
"""
Entry point for the plugin management functions
"""
- if request.POST.get('set_featured'):
+ if request.POST.get("set_featured"):
return plugin_set_featured(request, package_name)
- if request.POST.get('unset_featured'):
+ if request.POST.get("unset_featured"):
return plugin_unset_featured(request, package_name)
- if request.POST.get('delete'):
+ if request.POST.get("delete"):
return plugin_delete(request, package_name)
- return HttpResponseRedirect(reverse('user_details', args=[username]))
-
+ return HttpResponseRedirect(reverse("user_details", args=[username]))
###############################################
@@ -624,7 +728,7 @@ def user_block(request, username):
user.save()
msg = _("The user %s is now blocked." % user)
messages.success(request, msg, fail_silently=True)
- return HttpResponseRedirect(reverse('user_details', args=[user.username]))
+ return HttpResponseRedirect(reverse("user_details", args=[user.username]))
@staff_required
@@ -639,7 +743,7 @@ def user_unblock(request, username):
user.save()
msg = _("The user %s is now unblocked." % user)
messages.success(request, msg, fail_silently=True)
- return HttpResponseRedirect(reverse('user_details', args=[user.username]))
+ return HttpResponseRedirect(reverse("user_details", args=[user.username]))
@staff_required
@@ -649,11 +753,16 @@ def user_trust(request, username):
Assigns can_approve permission to the plugin creator
"""
user = get_object_or_404(User, username=username)
- user.user_permissions.add(Permission.objects.get(codename='can_approve', content_type=ContentType.objects.get(app_label="plugins", model='plugin')))
+ user.user_permissions.add(
+ Permission.objects.get(
+ codename="can_approve",
+ content_type=ContentType.objects.get(app_label="plugins", model="plugin"),
+ )
+ )
msg = _("The user %s is now a trusted user." % user)
messages.success(request, msg, fail_silently=True)
user_trust_notify(user)
- return HttpResponseRedirect(reverse('user_details', args=[user.username]))
+ return HttpResponseRedirect(reverse("user_details", args=[user.username]))
@staff_required
@@ -663,11 +772,16 @@ def user_untrust(request, username):
Revokes can_approve permission to the plugin creator
"""
user = get_object_or_404(User, username=username)
- user.user_permissions.remove(Permission.objects.get(codename='can_approve', content_type=ContentType.objects.get(app_label="plugins", model='plugin')))
+ user.user_permissions.remove(
+ Permission.objects.get(
+ codename="can_approve",
+ content_type=ContentType.objects.get(app_label="plugins", model="plugin"),
+ )
+ )
msg = _("The user %s is now an untrusted user." % user)
messages.success(request, msg, fail_silently=True)
user_trust_notify(user)
- return HttpResponseRedirect(reverse('user_details', args=[user.username]))
+ return HttpResponseRedirect(reverse("user_details", args=[user.username]))
@staff_required
@@ -676,16 +790,16 @@ def user_permissions_manage(request, username):
"""
Entry point for the user management functions
"""
- if request.POST.get('user_block'):
+ if request.POST.get("user_block"):
return user_block(request, username)
- if request.POST.get('user_unblock'):
+ if request.POST.get("user_unblock"):
return user_unblock(request, username)
- if request.POST.get('user_trust'):
+ if request.POST.get("user_trust"):
return user_trust(request, username)
- if request.POST.get('user_untrust'):
+ if request.POST.get("user_untrust"):
return user_untrust(request, username)
- return HttpResponseRedirect(reverse('user_details', args=[username]))
+ return HttpResponseRedirect(reverse("user_details", args=[username]))
###############################################
@@ -700,15 +814,17 @@ def _main_plugin_update(request, plugin, form):
Updates the main plugin object from version metadata
"""
# Update plugin from metadata
- for f in ['name', 'author', 'email', 'description', 'about', 'homepage', 'tracker']:
+ for f in ["name", "author", "email", "description", "about", "homepage", "tracker"]:
if form.cleaned_data.get(f):
setattr(plugin, f, form.cleaned_data.get(f))
# Icon has a special treatment
- if form.cleaned_data.get('icon_file'):
- setattr(plugin, 'icon', form.cleaned_data.get('icon_file'))
- if form.cleaned_data.get('tags'):
- plugin.tags.set(*[t.strip().lower() for t in form.cleaned_data.get('tags').split(',')])
+ if form.cleaned_data.get("icon_file"):
+ setattr(plugin, "icon", form.cleaned_data.get("icon_file"))
+ if form.cleaned_data.get("tags"):
+ plugin.tags.set(
+ *[t.strip().lower() for t in form.cleaned_data.get("tags").split(",")]
+ )
plugin.save()
@@ -721,12 +837,19 @@ def version_create(request, package_name):
"""
plugin = get_object_or_404(Plugin, package_name=package_name)
if not check_plugin_access(request.user, plugin):
- return render(request, 'plugins/version_permission_deny.html', { 'plugin' : plugin })
+ return render(
+ request, "plugins/version_permission_deny.html", {"plugin": plugin}
+ )
- version = PluginVersion(plugin = plugin, created_by = request.user)
- if request.method == 'POST':
+ version = PluginVersion(plugin=plugin, created_by=request.user)
+ if request.method == "POST":
- form = PluginVersionForm(request.POST, request.FILES, instance=version, is_trusted=request.user.has_perm('plugins.can_approve'))
+ form = PluginVersionForm(
+ request.POST,
+ request.FILES,
+ instance=version,
+ is_trusted=request.user.has_perm("plugins.can_approve"),
+ )
if form.is_valid():
try:
new_object = form.save()
@@ -734,13 +857,19 @@ def version_create(request, package_name):
messages.success(request, msg, fail_silently=True)
# The approved flag is also controlled in the form, but we
# are checking it here in any case for additional security
- if not request.user.has_perm('plugins.can_approve'):
+ if not request.user.has_perm("plugins.can_approve"):
new_object.approved = False
new_object.save()
- messages.warning(request, _('You do not have approval permissions, plugin version has been set unapproved.'), fail_silently=True)
+ messages.warning(
+ request,
+ _(
+ "You do not have approval permissions, plugin version has been set unapproved."
+ ),
+ fail_silently=True,
+ )
version_notify(new_object)
- if form.cleaned_data.get('icon_file'):
- form.cleaned_data['icon'] = form.cleaned_data.get('icon_file')
+ if form.cleaned_data.get("icon_file"):
+ form.cleaned_data["icon"] = form.cleaned_data.get("icon_file")
_main_plugin_update(request, new_object.plugin, form)
_check_optional_metadata(form, request)
return HttpResponseRedirect(new_object.plugin.get_absolute_url())
@@ -749,13 +878,15 @@ def version_create(request, package_name):
connection.close()
return HttpResponseRedirect(plugin.get_absolute_url())
else:
- form = PluginVersionForm(is_trusted=request.user.has_perm('plugins.can_approve'))
+ form = PluginVersionForm(
+ is_trusted=request.user.has_perm("plugins.can_approve")
+ )
- return render(request, 'plugins/version_form.html', {
- 'form' : form,
- 'plugin' : plugin,
- 'form_title' : _('New version for plugin')
- })
+ return render(
+ request,
+ "plugins/version_form.html",
+ {"form": form, "plugin": plugin, "form_title": _("New version for plugin")},
+ )
@login_required
@@ -763,13 +894,20 @@ def version_update(request, package_name, version):
"""
The form will update versions according to permissions
"""
- plugin = get_object_or_404(Plugin, package_name=package_name)
+ plugin = get_object_or_404(Plugin, package_name=package_name)
version = get_object_or_404(PluginVersion, plugin=plugin, version=version)
if not check_plugin_access(request.user, plugin):
- return render(request, 'plugins/version_permission_deny.html', { 'plugin' : plugin })
+ return render(
+ request, "plugins/version_permission_deny.html", {"plugin": plugin}
+ )
- if request.method == 'POST':
- form = PluginVersionForm(request.POST, request.FILES, instance=version, is_trusted=request.user.has_perm('plugins.can_approve'))
+ if request.method == "POST":
+ form = PluginVersionForm(
+ request.POST,
+ request.FILES,
+ instance=version,
+ is_trusted=request.user.has_perm("plugins.can_approve"),
+ )
if form.is_valid():
try:
new_object = form.save()
@@ -782,30 +920,40 @@ def version_update(request, package_name, version):
connection.close()
return HttpResponseRedirect(plugin.get_absolute_url())
else:
- form = PluginVersionForm(instance=version, is_trusted=request.user.has_perm('plugins.can_approve'))
-
- return render(request, 'plugins/version_form.html', {
- 'form' : form,
- 'plugin' : plugin,
- 'version' : version,
- 'form_title' : _('Edit version for plugin')
- })
+ form = PluginVersionForm(
+ instance=version, is_trusted=request.user.has_perm("plugins.can_approve")
+ )
+ return render(
+ request,
+ "plugins/version_form.html",
+ {
+ "form": form,
+ "plugin": plugin,
+ "version": version,
+ "form_title": _("Edit version for plugin"),
+ },
+ )
@login_required
def version_delete(request, package_name, version):
- plugin = get_object_or_404(Plugin, package_name=package_name)
+ plugin = get_object_or_404(Plugin, package_name=package_name)
version = get_object_or_404(PluginVersion, plugin=plugin, version=version)
if not check_plugin_access(request.user, plugin):
- return render(request, 'plugins/version_permission_deny.html', {})
- if 'delete_confirm' in request.POST:
+ return render(request, "plugins/version_permission_deny.html", {})
+ if "delete_confirm" in request.POST:
version.delete()
msg = _("The Plugin Version has been successfully deleted.")
messages.success(request, msg, fail_silently=True)
- return HttpResponseRedirect(reverse('plugin_detail', args=(plugin.package_name,)))
- return render(request, 'plugins/version_delete_confirm.html', { 'plugin' : plugin, 'version' : version })
-
+ return HttpResponseRedirect(
+ reverse("plugin_detail", args=(plugin.package_name,))
+ )
+ return render(
+ request,
+ "plugins/version_delete_confirm.html",
+ {"plugin": plugin, "version": version},
+ )
@login_required
@@ -814,7 +962,7 @@ def version_approve(request, package_name, version):
"""
Approves the plugin version
"""
- plugin = get_object_or_404(Plugin, package_name=package_name)
+ plugin = get_object_or_404(Plugin, package_name=package_name)
version = get_object_or_404(PluginVersion, plugin=plugin, version=version)
if not check_plugin_version_approval_rights(request.user, version.plugin):
msg = _("You do not have approval rights for this plugin.")
@@ -822,11 +970,11 @@ def version_approve(request, package_name, version):
return HttpResponseRedirect(version.get_absolute_url())
version.approved = True
version.save()
- msg = _("The plugin version \"%s\" is now approved" % version)
+ msg = _('The plugin version "%s" is now approved' % version)
messages.success(request, msg, fail_silently=True)
plugin_approve_notify(version.plugin, msg, request.user)
try:
- redirect_to = request.META['HTTP_REFERER']
+ redirect_to = request.META["HTTP_REFERER"]
except:
redirect_to = version.get_absolute_url()
return HttpResponseRedirect(redirect_to)
@@ -838,7 +986,7 @@ def version_unapprove(request, package_name, version):
"""
unapproves the plugin version
"""
- plugin = get_object_or_404(Plugin, package_name=package_name)
+ plugin = get_object_or_404(Plugin, package_name=package_name)
version = get_object_or_404(PluginVersion, plugin=plugin, version=version)
if not check_plugin_version_approval_rights(request.user, version.plugin):
msg = _("You do not have approval rights for this plugin.")
@@ -846,36 +994,35 @@ def version_unapprove(request, package_name, version):
return HttpResponseRedirect(version.get_absolute_url())
version.approved = False
version.save()
- msg = _("The plugin version \"%s\" is now unapproved" % version)
+ msg = _('The plugin version "%s" is now unapproved' % version)
messages.success(request, msg, fail_silently=True)
plugin_approve_notify(version.plugin, msg, request.user)
try:
- redirect_to = request.META['HTTP_REFERER']
+ redirect_to = request.META["HTTP_REFERER"]
except:
redirect_to = version.get_absolute_url()
return HttpResponseRedirect(redirect_to)
-
@login_required
@require_POST
def version_manage(request, package_name, version):
"""
Entry point for the user management functions
"""
- if 'version_approve' in request.POST:
+ if "version_approve" in request.POST:
return version_approve(request, package_name, version)
- if 'version_unapprove' in request.POST:
+ if "version_unapprove" in request.POST:
return version_unapprove(request, package_name, version)
- return HttpResponseRedirect(reverse('plugin_detail', args=[package_name]))
+ return HttpResponseRedirect(reverse("plugin_detail", args=[package_name]))
def version_download(request, package_name, version):
"""
Update download counter(s)
"""
- plugin = get_object_or_404(Plugin, package_name=package_name)
+ plugin = get_object_or_404(Plugin, package_name=package_name)
version = get_object_or_404(PluginVersion, plugin=plugin, version=version)
version.downloads = version.downloads + 1
version.save()
@@ -884,10 +1031,13 @@ def version_download(request, package_name, version):
plugin.save(keep_date=True)
if not version.package.file.file.closed:
version.package.file.file.close()
- zipfile = open(version.package.file.name, 'rb')
+ zipfile = open(version.package.file.name, "rb")
file_content = zipfile.read()
- response = HttpResponse(file_content, content_type='application/zip')
- response['Content-Disposition'] = 'attachment; filename=%s-%s.zip' % (version.plugin.package_name, version.version)
+ response = HttpResponse(file_content, content_type="application/zip")
+ response["Content-Disposition"] = "attachment; filename=%s-%s.zip" % (
+ version.plugin.package_name,
+ version.version,
+ )
return response
@@ -895,9 +1045,9 @@ def version_detail(request, package_name, version):
"""
Show version details
"""
- plugin = get_object_or_404(Plugin, package_name=package_name)
+ plugin = get_object_or_404(Plugin, package_name=package_name)
version = get_object_or_404(PluginVersion, plugin=plugin, version=version)
- return render(request, 'plugins/version_detail.html', {'version' : version })
+ return render(request, "plugins/version_detail.html", {"version": version})
###############################################
@@ -921,69 +1071,108 @@ def xml_plugins(request, qg_version=None, stable_only=None, package_name=None):
* package_name: Plugin.package_name
"""
- qg_version = qg_version if qg_version is not None else vjust(request.GET.get('qgis', '1.8.0'), fillchar='0', level=2, force_zero=True)
- stable_only = stable_only if stable_only is not None else request.GET.get('stable_only', '0')
- package_name = package_name if package_name is not None else request.GET.get('package_name', None)
+ qg_version = (
+ qg_version
+ if qg_version is not None
+ else vjust(
+ request.GET.get("qgis", "1.8.0"), fillchar="0", level=2, force_zero=True
+ )
+ )
+ stable_only = (
+ stable_only if stable_only is not None else request.GET.get("stable_only", "0")
+ )
+ package_name = (
+ package_name
+ if package_name is not None
+ else request.GET.get("package_name", None)
+ )
filters = {}
version_filters = {}
object_list = []
if qg_version:
- filters.update({'pluginversion__min_qg_version__lte' : qg_version})
- version_filters.update({'min_qg_version__lte' : qg_version})
- filters.update({'pluginversion__max_qg_version__gte' : qg_version})
- version_filters.update({'max_qg_version__gte' : qg_version})
-
+ filters.update({"pluginversion__min_qg_version__lte": qg_version})
+ version_filters.update({"min_qg_version__lte": qg_version})
+ filters.update({"pluginversion__max_qg_version__gte": qg_version})
+ version_filters.update({"max_qg_version__gte": qg_version})
# Get all versions for the given plugin)
if package_name:
- filters.update({'package_name' : package_name})
+ filters.update({"package_name": package_name})
try:
plugin = Plugin.approved_objects.get(**filters)
plugin_version_filters = copy.copy(version_filters)
- plugin_version_filters.update({'plugin' : plugin})
- for plugin_version in PluginVersion.stable_objects.filter(**plugin_version_filters):
+ plugin_version_filters.update({"plugin": plugin})
+ for plugin_version in PluginVersion.stable_objects.filter(
+ **plugin_version_filters
+ ):
object_list.append(plugin_version)
- if stable_only != '1':
- for plugin_version in PluginVersion.experimental_objects.filter(**plugin_version_filters):
+ if stable_only != "1":
+ for plugin_version in PluginVersion.experimental_objects.filter(
+ **plugin_version_filters
+ ):
object_list.append(plugin_version)
except Plugin.DoesNotExist:
pass
else:
# Checked the cached plugins
- qgis_version = request.GET.get('qgis', None)
- qgis_filename = 'plugins_{}.xml'.format(qgis_version)
- folder_name = os.path.join(
- settings.MEDIA_ROOT,
- 'cached_xmls'
- )
+ qgis_version = request.GET.get("qgis", None)
+ qgis_filename = "plugins_{}.xml".format(qgis_version)
+ folder_name = os.path.join(settings.MEDIA_ROOT, "cached_xmls")
path_file = os.path.join(folder_name, qgis_filename)
if os.path.exists(path_file):
- return HttpResponse(
- open(path_file).read(), content_type='application/xml')
-
- trusted_users_ids = list(zip(*User.objects.filter(Q(user_permissions__codename='can_approve', user_permissions__content_type__app_label='plugins') | Q(is_superuser=True)).distinct().values_list('id')))[0]
- qs = Plugin.approved_objects.filter(**filters).annotate(is_trusted=RawSQL('%s.created_by_id in (%s)' % (Plugin._meta.db_table, (',').join([str(tu) for tu in trusted_users_ids])), ()))
+ return HttpResponse(open(path_file).read(), content_type="application/xml")
+
+ trusted_users_ids = list(
+ zip(
+ *User.objects.filter(
+ Q(
+ user_permissions__codename="can_approve",
+ user_permissions__content_type__app_label="plugins",
+ )
+ | Q(is_superuser=True)
+ )
+ .distinct()
+ .values_list("id")
+ )
+ )[0]
+ qs = Plugin.approved_objects.filter(**filters).annotate(
+ is_trusted=RawSQL(
+ "%s.created_by_id in (%s)"
+ % (
+ Plugin._meta.db_table,
+ (",").join([str(tu) for tu in trusted_users_ids]),
+ ),
+ (),
+ )
+ )
for plugin in qs:
plugin_version_filters = copy.copy(version_filters)
- plugin_version_filters.update({'plugin_id' : plugin.pk})
+ plugin_version_filters.update({"plugin_id": plugin.pk})
try:
data = PluginVersion.stable_objects.filter(**plugin_version_filters)[0]
- setattr(data, 'is_trusted', plugin.is_trusted)
+ setattr(data, "is_trusted", plugin.is_trusted)
object_list.append(data)
except IndexError:
pass
- if stable_only != '1':
+ if stable_only != "1":
try:
- data = PluginVersion.experimental_objects.filter(**plugin_version_filters)[0]
- setattr(data, 'is_trusted', plugin.is_trusted)
+ data = PluginVersion.experimental_objects.filter(
+ **plugin_version_filters
+ )[0]
+ setattr(data, "is_trusted", plugin.is_trusted)
object_list.append(data)
except IndexError:
pass
- return render(request, 'plugins/plugins.xml', {'object_list': object_list}, content_type='text/xml')
+ return render(
+ request,
+ "plugins/plugins.xml",
+ {"object_list": object_list},
+ content_type="text/xml",
+ )
@cache_page(60 * 15)
@@ -998,31 +1187,47 @@ def xml_plugins_new(request, qg_version=None, stable_only=None, package_name=Non
* package_name: Plugin.package_name
"""
- qg_version = qg_version if qg_version is not None else vjust(request.GET.get('qgis', '1.8.0'), fillchar='0', level=2, force_zero=True)
- stable_only = stable_only if stable_only is not None else request.GET.get('stable_only', '0')
- package_name = package_name if package_name is not None else request.GET.get('package_name', None)
+ qg_version = (
+ qg_version
+ if qg_version is not None
+ else vjust(
+ request.GET.get("qgis", "1.8.0"), fillchar="0", level=2, force_zero=True
+ )
+ )
+ stable_only = (
+ stable_only if stable_only is not None else request.GET.get("stable_only", "0")
+ )
+ package_name = (
+ package_name
+ if package_name is not None
+ else request.GET.get("package_name", None)
+ )
filters = {}
version_filters = {}
object_list = []
if qg_version:
- filters.update({'pluginversion__min_qg_version__lte' : qg_version})
- version_filters.update({'min_qg_version__lte' : qg_version})
- filters.update({'pluginversion__max_qg_version__gte' : qg_version})
- version_filters.update({'max_qg_version__gte' : qg_version})
+ filters.update({"pluginversion__min_qg_version__lte": qg_version})
+ version_filters.update({"min_qg_version__lte": qg_version})
+ filters.update({"pluginversion__max_qg_version__gte": qg_version})
+ version_filters.update({"max_qg_version__gte": qg_version})
# Get all versions for the given plugin
if package_name:
- filters.update({'package_name' : package_name})
+ filters.update({"package_name": package_name})
try:
plugin = Plugin.approved_objects.get(**filters)
plugin_version_filters = copy.copy(version_filters)
- plugin_version_filters.update({'plugin' : plugin})
- for plugin_version in PluginVersion.stable_objects.filter(**plugin_version_filters):
+ plugin_version_filters.update({"plugin": plugin})
+ for plugin_version in PluginVersion.stable_objects.filter(
+ **plugin_version_filters
+ ):
object_list.append(plugin_version)
- if stable_only != '1':
- for plugin_version in PluginVersion.experimental_objects.filter(**plugin_version_filters):
+ if stable_only != "1":
+ for plugin_version in PluginVersion.experimental_objects.filter(
+ **plugin_version_filters
+ ):
object_list.append(plugin_version)
except Plugin.DoesNotExist:
pass
@@ -1031,7 +1236,7 @@ def xml_plugins_new(request, qg_version=None, stable_only=None, package_name=Non
# Fast lane: uses raw queries
- trusted_users_ids = '''
+ trusted_users_ids = """
(SELECT DISTINCT "auth_user"."id"
FROM "auth_user"
LEFT OUTER JOIN "auth_user_user_permissions"
@@ -1043,9 +1248,9 @@ def xml_plugins_new(request, qg_version=None, stable_only=None, package_name=Non
WHERE (("auth_permission"."codename" = 'can_approve'
AND "django_content_type"."app_label" = 'plugins')
OR "auth_user"."is_superuser" = True))
- '''
+ """
- sql = '''
+ sql = """
SELECT DISTINCT ON (pv.plugin_id) pv.*,
pv.created_by_id IN %(trusted_users_ids)s AS is_trusted
FROM %(pv_table)s pv
@@ -1056,28 +1261,39 @@ def xml_plugins_new(request, qg_version=None, stable_only=None, package_name=Non
AND pv.experimental = %(experimental)s
)
ORDER BY pv.plugin_id, pv.version DESC
- '''
-
-
- object_list_new = PluginVersion.objects.raw(sql % {
- 'pv_table': PluginVersion._meta.db_table,
- 'p_table': Plugin._meta.db_table,
- 'qg_version': qg_version,
- 'experimental': 'False',
- 'trusted_users_ids': str(trusted_users_ids),
- })
+ """
+ object_list_new = PluginVersion.objects.raw(
+ sql
+ % {
+ "pv_table": PluginVersion._meta.db_table,
+ "p_table": Plugin._meta.db_table,
+ "qg_version": qg_version,
+ "experimental": "False",
+ "trusted_users_ids": str(trusted_users_ids),
+ }
+ )
- if stable_only != '1':
+ if stable_only != "1":
# Do the query
object_list_new = [o for o in object_list_new]
- object_list_new += [o for o in PluginVersion.objects.raw(sql % {
- 'pv_table': PluginVersion._meta.db_table,
- 'p_table': Plugin._meta.db_table,
- 'qg_version': qg_version,
- 'experimental': 'True',
- 'trusted_users_ids': str(trusted_users_ids),
- })]
-
-
- return render(request, 'plugins/plugins.xml', {'object_list': object_list_new}, content_type='text/xml')
+ object_list_new += [
+ o
+ for o in PluginVersion.objects.raw(
+ sql
+ % {
+ "pv_table": PluginVersion._meta.db_table,
+ "p_table": Plugin._meta.db_table,
+ "qg_version": qg_version,
+ "experimental": "True",
+ "trusted_users_ids": str(trusted_users_ids),
+ }
+ )
+ ]
+
+ return render(
+ request,
+ "plugins/plugins.xml",
+ {"object_list": object_list_new},
+ content_type="text/xml",
+ )
diff --git a/qgis-app/qgis_context_processor.py b/qgis-app/qgis_context_processor.py
index 1bf50e27..11ff0481 100644
--- a/qgis-app/qgis_context_processor.py
+++ b/qgis-app/qgis_context_processor.py
@@ -1,23 +1,23 @@
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
-import re
+
def additions(request):
"""Insert some additional information into the template context
from the settings and set the base template according to qs.
"""
- if request.is_ajax() or request.GET.get('ajax'):
- base_template = 'ajax_base.html'
+ if request.is_ajax() or request.GET.get("ajax"):
+ base_template = "ajax_base.html"
is_naked = True
else:
- base_template = 'base.html'
+ base_template = "base.html"
is_naked = False
additions = {
- 'BASE_TEMPLATE': base_template,
- 'IS_NAKED': is_naked,
- 'DEBUG': settings.DEBUG,
+ "BASE_TEMPLATE": base_template,
+ "IS_NAKED": is_naked,
+ "DEBUG": settings.DEBUG,
}
return additions
diff --git a/qgis-app/search_sites.py b/qgis-app/search_sites.py
index 3afb2faa..59580c73 100644
--- a/qgis-app/search_sites.py
+++ b/qgis-app/search_sites.py
@@ -1,3 +1,3 @@
import haystack
-haystack.autodiscover()
+haystack.autodiscover()
diff --git a/qgis-app/settings.py b/qgis-app/settings.py
index 9250f602..54d76a4a 100644
--- a/qgis-app/settings.py
+++ b/qgis-app/settings.py
@@ -2,6 +2,7 @@
# Django settings for qgis project.
# ABP: More portable config
import os
+
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
TEMPLATE_DEBUG = False
@@ -14,11 +15,11 @@
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
-TIME_ZONE = 'America/Chicago'
+TIME_ZONE = "America/Chicago"
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
SITE_ID = 1
@@ -32,21 +33,21 @@
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = SITE_ROOT + '/static/'
+MEDIA_ROOT = SITE_ROOT + "/static/"
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
-MEDIA_URL = '/static/'
-MEDIA_URL_FOLDER = '/static/'
+MEDIA_URL = "/static/"
+MEDIA_URL_FOLDER = "/static/"
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = '/admin/'
+ADMIN_MEDIA_PREFIX = "/admin/"
-STATIC_URL='/static_media/'
-STATIC_ROOT=SITE_ROOT + '/static_media/'
+STATIC_URL = "/static_media/"
+STATIC_ROOT = SITE_ROOT + "/static_media/"
STATICFILES_DIRS = [
@@ -54,117 +55,115 @@
]
STATICFILES_FINDERS = [
- 'django.contrib.staticfiles.finders.FileSystemFinder',
- 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+ "django.contrib.staticfiles.finders.FileSystemFinder",
+ "django.contrib.staticfiles.finders.AppDirectoriesFinder",
]
# Make this unique, and don't share it with anybody.
-SECRET_KEY = 'y2vu=4qarl)p=g_blq_c4afk!p6u_cor1gy1k@05ro=+tf7+)g'
+SECRET_KEY = "y2vu=4qarl)p=g_blq_c4afk!p6u_cor1gy1k@05ro=+tf7+)g"
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.Loader',
- 'django.template.loaders.app_directories.Loader',
- 'django.template.loaders.eggs.Loader', #Tim: needed on live server for CAB
+ "django.template.loaders.filesystem.Loader",
+ "django.template.loaders.app_directories.Loader",
+ "django.template.loaders.eggs.Loader", # Tim: needed on live server for CAB
)
MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.middleware.cache.UpdateCacheMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
+ "django.middleware.security.SecurityMiddleware",
+ "django.middleware.cache.UpdateCacheMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
# Needed by rpc4django
- 'plugins.middleware.HttpAuthMiddleware',
- 'django.contrib.auth.middleware.RemoteUserMiddleware',
- 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
+ "plugins.middleware.HttpAuthMiddleware",
+ "django.contrib.auth.middleware.RemoteUserMiddleware",
+ "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware",
# Added by Tim for advanced loggin options
- 'django.middleware.cache.FetchFromCacheMiddleware',
- 'middleware.XForwardedForMiddleware',
+ "django.middleware.cache.FetchFromCacheMiddleware",
+ "middleware.XForwardedForMiddleware",
]
-ROOT_URLCONF = 'urls'
+ROOT_URLCONF = "urls"
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
- os.path.join(SITE_ROOT, 'templates'),
+ os.path.join(SITE_ROOT, "templates"),
)
INSTALLED_APPS = [
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.messages',
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.sites",
+ "django.contrib.messages",
# Uncomment the next line to enable the admin:
- 'django.contrib.admin',
+ "django.contrib.admin",
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
- 'django.contrib.staticfiles',
-
+ "django.contrib.staticfiles",
# ABP:
- 'plugins',
+ "plugins",
#'pagination',
- 'django.contrib.humanize',
+ "django.contrib.humanize",
#'django.contrib.markup',
- 'django.contrib.syndication',
+ "django.contrib.syndication",
#'ratings',
- 'taggit',
- 'taggit_autosuggest',
- 'taggit_templatetags',
- 'haystack',
- 'django.contrib.flatpages',
- 'simplemenu',
- 'tinymce',
+ "taggit",
+ "taggit_autosuggest",
+ "taggit_templatetags",
+ "haystack",
+ "django.contrib.flatpages",
+ "simplemenu",
+ "tinymce",
# Tim for django snippets app support
#'cab', #the django snippets app itself
- 'debug_toolbar',
+ "debug_toolbar",
# Tim for command extensions so we can run feedjack cron using python manage.py runscript
- 'django_extensions',
+ "django_extensions",
# Sam for Users map
#'django.contrib.gis',
#'users',
- 'olwidget',
+ "olwidget",
# Tim for blog planet / feed aggregator
- 'feedjack',
+ "feedjack",
# For users app thumbs
- 'sorl.thumbnail',
+ "sorl.thumbnail",
# RPC
- 'rpc4django',
+ "rpc4django",
#'south',
- 'djangoratings',
- 'lib',
- 'endless_pagination',
- 'userexport',
- 'bootstrap_pagination',
- 'sortable_listview',
-
- 'user_map',
- 'leaflet',
- 'bootstrapform',
- 'rest_framework',
- 'rest_framework_gis',
- 'preferences',
+ "djangoratings",
+ "lib",
+ "endless_pagination",
+ "userexport",
+ "bootstrap_pagination",
+ "sortable_listview",
+ "user_map",
+ "leaflet",
+ "bootstrapform",
+ "rest_framework",
+ "rest_framework_gis",
+ "preferences",
# styles:
- 'styles',
+ "styles",
]
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': ['templates'],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors' : (
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": ["templates"],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": (
"django.contrib.auth.context_processors.auth",
- 'django.contrib.messages.context_processors.messages',
- 'django.template.context_processors.request',
+ "django.contrib.messages.context_processors.messages",
+ "django.template.context_processors.request",
# ABP: adds DEBUG and BASE_TEMPLATE vars
"qgis_context_processor.additions",
"preferences.context_processors.preferences_cp",
@@ -173,53 +172,55 @@
},
]
-ACCOUNT_ACTIVATION_DAYS = 7 # One-week activation window; you may, of course, use a different value
+ACCOUNT_ACTIVATION_DAYS = (
+ 7 # One-week activation window; you may, of course, use a different value
+)
-LOGIN_REDIRECT_URL='/'
+LOGIN_REDIRECT_URL = "/"
# Added by Tim for snippets (and possibly other site search support)
-#HAYSTACK_SITECONF = 'search_sites'
+# HAYSTACK_SITECONF = 'search_sites'
# django.core.exceptions.ImproperlyConfigured: The HAYSTACK_SITECONF setting is no longer used & can be removed.
-HAYSTACK_CONNECTIONS = { 'default' : 'whoosh' }
-HAYSTACK_WHOOSH_PATH = SITE_ROOT + '/search-index'
+HAYSTACK_CONNECTIONS = {"default": "whoosh"}
+HAYSTACK_WHOOSH_PATH = SITE_ROOT + "/search-index"
HAYSTACK_CONNECTIONS = {
- 'default': {
- 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
- 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
+ "default": {
+ "ENGINE": "haystack.backends.whoosh_backend.WhooshEngine",
+ "PATH": os.path.join(os.path.dirname(__file__), "whoosh_index"),
},
}
# Migration: see http://django-haystack.readthedocs.org/en/latest/migration_from_1_to_2.html#removal-of-realtimesearchindex
-HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
+HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
# Added by Tim for database based caching
# See http://docs.djangoproject.com/en/dev/topics/cache/
CACHES = {
- 'default': {
- #'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
- 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
- 'LOCATION': 'cache_table',
- }
+ "default": {
+ #'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
+ "BACKEND": "django.core.cache.backends.dummy.DummyCache",
+ "LOCATION": "cache_table",
+ }
}
CACHE_MIDDLEWARE_SECONDS = 600
-CACHE_MIDDLEWARE_PREFIX = ''
-CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True
+CACHE_MIDDLEWARE_PREFIX = ""
+CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
-TAGGIT_TAGCLOUD_MIN=10
-TAGGIT_TAGCLOUD_MAX=30
+TAGGIT_TAGCLOUD_MIN = 10
+TAGGIT_TAGCLOUD_MAX = 30
-INTERNAL_IPS = ('127.0.0.1',)
+INTERNAL_IPS = ("127.0.0.1",)
-DEFAULT_FROM_EMAIL='noreply@qgis.org'
+DEFAULT_FROM_EMAIL = "noreply@qgis.org"
-#TINYMCE_JS_URL = 'http://debug.example.org/tiny_mce/tiny_mce_src.js'
+# TINYMCE_JS_URL = 'http://debug.example.org/tiny_mce/tiny_mce_src.js'
TINYMCE_DEFAULT_CONFIG = {
- 'plugins': "table,spellchecker,paste,searchreplace",
- 'theme': "advanced",
+ "plugins": "table,spellchecker,paste,searchreplace",
+ "theme": "advanced",
}
TINYMCE_SPELLCHECKER = True
TINYMCE_COMPRESSOR = True
@@ -231,77 +232,61 @@
# rpc4django
RPC4DJANGO_LOG_REQUESTS_RESPONSES = False
-#QGIS dev list email address for plugins approval notifications
-QGIS_DEV_MAILING_LIST_ADDRESS=''
+# QGIS dev list email address for plugins approval notifications
+QGIS_DEV_MAILING_LIST_ADDRESS = ""
# Media URL for taggit autocomplete
-TAGGIT_AUTOCOMPLETE_JS_BASE_URL=MEDIA_ROOT + '/taggit-autocomplete'
+TAGGIT_AUTOCOMPLETE_JS_BASE_URL = MEDIA_ROOT + "/taggit-autocomplete"
# Taggit: exclude tags with less than specified tagged items
-TAGCLOUD_COUNT_GTE=3
+TAGCLOUD_COUNT_GTE = 3
# ratings
-RATINGS_VOTES_PER_IP=10000
+RATINGS_VOTES_PER_IP = 10000
-#OLWIDGET_STATIC_URL='/static/olwidget/'
+# OLWIDGET_STATIC_URL='/static/olwidget/'
-DEBUG_TOOLBAR_CONFIG = {
- 'INTERCEPT_REDIRECTS': False
-}
+DEBUG_TOOLBAR_CONFIG = {"INTERCEPT_REDIRECTS": False}
-THUMBNAIL_ENGINE='sorl.thumbnail.engines.convert_engine.Engine'
+THUMBNAIL_ENGINE = "sorl.thumbnail.engines.convert_engine.Engine"
USER_MAP = {
- 'project_name': 'QGIS',
- 'favicon_file': '/static/images/qgis-icon-32x32.png',
- 'login_view': 'login',
- 'marker': {
- 'iconUrl': '/static/images/qgis-icon-32x32.png',
- 'iconSize': [32, 32],
- 'popupAnchor': [0, -15]
+ "project_name": "QGIS",
+ "favicon_file": "/static/images/qgis-icon-32x32.png",
+ "login_view": "login",
+ "marker": {
+ "iconUrl": "/static/images/qgis-icon-32x32.png",
+ "iconSize": [32, 32],
+ "popupAnchor": [0, -15],
},
- 'leaflet_config': {
- 'TILES': [(
- # The title
- 'OpenStreetMap',
- # Tile's URL
- 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
- # More valid leaflet option is passed here
- {
- 'attribution': '© OpenStreetMap'
- ' and contributors, under an open '
- 'license',
- 'maxZoom': 18,
- 'minZoom': 2,
- 'noWrap': True
-
- }
- )]
+ "leaflet_config": {
+ "TILES": [
+ (
+ # The title
+ "OpenStreetMap",
+ # Tile's URL
+ "http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
+ # More valid leaflet option is passed here
+ {
+ "attribution": '© OpenStreetMap'
+ " and contributors, under an open '
+ "license",
+ "maxZoom": 18,
+ "minZoom": 2,
+ "noWrap": True,
+ },
+ )
+ ]
},
- 'roles': [
- {
- 'id': 1,
- 'name': 'User',
- 'badge': 'user_map/img/badge-user.png'
- },
- {
- 'id': 2,
- 'name': 'Trainer',
- 'badge': 'user_map/img/badge-trainer.png'
- },
- {
- 'id': 3,
- 'name': 'Developer',
- 'badge': 'user_map/img/badge-developer.png'
- }
+ "roles": [
+ {"id": 1, "name": "User", "badge": "user_map/img/badge-user.png"},
+ {"id": 2, "name": "Trainer", "badge": "user_map/img/badge-trainer.png"},
+ {"id": 3, "name": "Developer", "badge": "user_map/img/badge-developer.png"},
],
- 'api_user_fields': [
- 'username'
- ],
-
+ "api_user_fields": ["username"],
}
@@ -323,24 +308,24 @@
# Local settings might have enabled DEBUG, load additional modules here
if DEBUG:
- INSTALLED_APPS.append('debug_toolbar')
- MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
+ INSTALLED_APPS.append("debug_toolbar")
+ MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")
DEBUG_TOOLBAR_PANELS = [
- 'debug_toolbar.panels.versions.VersionsPanel',
- 'debug_toolbar.panels.timer.TimerPanel',
- 'debug_toolbar.panels.settings.SettingsPanel',
- 'debug_toolbar.panels.headers.HeadersPanel',
- 'debug_toolbar.panels.request.RequestPanel',
- 'debug_toolbar.panels.sql.SQLPanel',
- 'debug_toolbar.panels.staticfiles.StaticFilesPanel',
- 'debug_toolbar.panels.templates.TemplatesPanel',
- 'debug_toolbar.panels.cache.CachePanel',
- 'debug_toolbar.panels.signals.SignalsPanel',
- 'debug_toolbar.panels.logging.LoggingPanel',
- 'debug_toolbar.panels.redirects.RedirectsPanel',
+ "debug_toolbar.panels.versions.VersionsPanel",
+ "debug_toolbar.panels.timer.TimerPanel",
+ "debug_toolbar.panels.settings.SettingsPanel",
+ "debug_toolbar.panels.headers.HeadersPanel",
+ "debug_toolbar.panels.request.RequestPanel",
+ "debug_toolbar.panels.sql.SQLPanel",
+ "debug_toolbar.panels.staticfiles.StaticFilesPanel",
+ "debug_toolbar.panels.templates.TemplatesPanel",
+ "debug_toolbar.panels.cache.CachePanel",
+ "debug_toolbar.panels.signals.SignalsPanel",
+ "debug_toolbar.panels.logging.LoggingPanel",
+ "debug_toolbar.panels.redirects.RedirectsPanel",
]
-BROKER_URL = 'amqp://guest:guest@%s:5672//' % os.environ['RABBITMQ_HOST']
+BROKER_URL = "amqp://guest:guest@%s:5672//" % os.environ["RABBITMQ_HOST"]
RESULT_BACKEND = BROKER_URL
CELERY_BROKER_URL = BROKER_URL
CELERY_RESULT_BACKEND = CELERY_BROKER_URL
diff --git a/qgis-app/settings_auth.py b/qgis-app/settings_auth.py
index fd6e521e..6c2dd8f2 100644
--- a/qgis-app/settings_auth.py
+++ b/qgis-app/settings_auth.py
@@ -1,32 +1,33 @@
+import ast
+import os
+
import ldap
-import ast, os
-from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
+from django_auth_ldap.config import GroupOfNamesType, LDAPSearch
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
# Keep ModelBackend around for per-user permissions and maybe a local superuser.
AUTHENTICATION_BACKENDS = (
- 'django.contrib.auth.backends.ModelBackend',
- 'django.contrib.auth.backends.RemoteUserBackend',
+ "django.contrib.auth.backends.ModelBackend",
+ "django.contrib.auth.backends.RemoteUserBackend",
)
-if ast.literal_eval(os.environ.get('ENABLE_LDAP', 'False')):
- AUTHENTICATION_BACKENDS = (
- 'django_auth_ldap.backend.LDAPBackend',
- ) + AUTHENTICATION_BACKENDS
- # Baseline configuration.
- AUTH_LDAP_SERVER_URI = "ldaps://ldap.osgeo.org"
+if ast.literal_eval(os.environ.get("ENABLE_LDAP", "False")):
+ AUTHENTICATION_BACKENDS = (
+ "django_auth_ldap.backend.LDAPBackend",
+ ) + AUTHENTICATION_BACKENDS
+ # Baseline configuration.
+ AUTH_LDAP_SERVER_URI = "ldaps://ldap.osgeo.org"
AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=People,dc=osgeo,dc=org"
# Populate the Django user from the LDAP directory.
AUTH_LDAP_USER_ATTR_MAP = {
- "first_name": "givenName",
- "last_name": "sn",
- "email": "mail"
+ "first_name": "givenName",
+ "last_name": "sn",
+ "email": "mail",
}
# Cache group memberships for an hour to minimize LDAP traffic
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
-
diff --git a/qgis-app/settings_docker.py b/qgis-app/settings_docker.py
index 2200839a..afb1c9e9 100644
--- a/qgis-app/settings_docker.py
+++ b/qgis-app/settings_docker.py
@@ -1,127 +1,122 @@
-from settings import *
import ast
import os
+
+from settings import *
+
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
-DEBUG = ast.literal_eval(os.environ.get('DEBUG', 'True'))
+DEBUG = ast.literal_eval(os.environ.get("DEBUG", "True"))
THUMBNAIL_DEBUG = DEBUG
-ALLOWED_HOSTS = ['*']
+ALLOWED_HOSTS = ["*"]
# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/var/www/example.com/media/"
-MEDIA_ROOT = '/home/web/media/'
+MEDIA_ROOT = "/home/web/media/"
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://example.com/media/", "http://media.example.com/"
# MEDIA_URL = '/media/'
# setting full MEDIA_URL to be able to use it for the feeds
-MEDIA_URL = '/media/'
+MEDIA_URL = "/media/"
# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
-STATIC_ROOT = '/home/web/static'
+STATIC_ROOT = "/home/web/static"
# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
-STATIC_URL = '/static/'
+STATIC_URL = "/static/"
INSTALLED_APPS = [
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.messages',
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.sites",
+ "django.contrib.messages",
# Uncomment the next line to enable the admin:
- 'django.contrib.admin',
+ "django.contrib.admin",
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
- 'django.contrib.staticfiles',
- 'django.contrib.flatpages',
+ "django.contrib.staticfiles",
+ "django.contrib.flatpages",
# full text search postgres
- 'django.contrib.postgres',
-
+ "django.contrib.postgres",
# ABP:
- 'plugins',
- 'django.contrib.humanize',
- 'django.contrib.syndication',
- 'bootstrap_pagination',
- 'sortable_listview',
- 'lib', # Container for small tags and functions
- 'sorl.thumbnail',
- 'djangoratings',
-
- 'taggit',
- 'taggit_autosuggest',
- 'taggit_templatetags',
- 'haystack',
- 'simplemenu',
- 'tinymce',
- 'rpc4django',
-
- 'feedjack',
-
- 'preferences',
-
- 'rest_framework',
- 'sorl_thumbnail_serializer', # serialize image
- 'drf_multiple_model',
- 'drf_yasg',
-
- 'api',
-
+ "plugins",
+ "django.contrib.humanize",
+ "django.contrib.syndication",
+ "bootstrap_pagination",
+ "sortable_listview",
+ "lib", # Container for small tags and functions
+ "sorl.thumbnail",
+ "djangoratings",
+ "taggit",
+ "taggit_autosuggest",
+ "taggit_templatetags",
+ "haystack",
+ "simplemenu",
+ "tinymce",
+ "rpc4django",
+ "feedjack",
+ "preferences",
+ "rest_framework",
+ "sorl_thumbnail_serializer", # serialize image
+ "drf_multiple_model",
+ "drf_yasg",
+ "api",
# styles:
- 'styles',
+ "styles",
# geopackages
- 'geopackages',
+ "geopackages",
# QGIS Layer Definition File (.qlr)
- 'layerdefinitions',
+ "layerdefinitions",
# models (sharing .model3 file feature)
- 'models',
- 'wavefronts'
+ "models",
+ "wavefronts",
]
DATABASES = {
- 'default': {
- 'ENGINE': 'django.contrib.gis.db.backends.postgis',
- 'NAME': os.environ['DATABASE_NAME'],
- 'USER': os.environ['DATABASE_USERNAME'],
- 'PASSWORD': os.environ['DATABASE_PASSWORD'],
- 'HOST': os.environ['DATABASE_HOST'],
- 'PORT': 5432,
- 'TEST': {
- 'NAME': 'unittests',
- }
+ "default": {
+ "ENGINE": "django.contrib.gis.db.backends.postgis",
+ "NAME": os.environ["DATABASE_NAME"],
+ "USER": os.environ["DATABASE_USERNAME"],
+ "PASSWORD": os.environ["DATABASE_PASSWORD"],
+ "HOST": os.environ["DATABASE_HOST"],
+ "PORT": 5432,
+ "TEST": {
+ "NAME": "unittests",
+ },
}
}
-PAGINATION_DEFAULT_PAGINATION=20
-PAGINATION_DEFAULT_PAGINATION_HUB=30
-LOGIN_REDIRECT_URL='/'
+PAGINATION_DEFAULT_PAGINATION = 20
+PAGINATION_DEFAULT_PAGINATION_HUB = 30
+LOGIN_REDIRECT_URL = "/"
SERVE_STATIC_MEDIA = DEBUG
-DEFAULT_PLUGINS_SITE = os.environ.get('DEFAULT_PLUGINS_SITE', '')
+DEFAULT_PLUGINS_SITE = os.environ.get("DEFAULT_PLUGINS_SITE", "")
# See fig.yml file for postfix container definition
#
-EMAIL_BACKEND = os.environ.get('EMAIL_BACKEND',
- 'django.core.mail.backends.smtp.EmailBackend')
+EMAIL_BACKEND = os.environ.get(
+ "EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend"
+)
# Host for sending e-mail.
-EMAIL_HOST = os.environ.get('EMAIL_HOST', 'smtp')
+EMAIL_HOST = os.environ.get("EMAIL_HOST", "smtp")
# Port for sending e-mail.
-EMAIL_PORT = int(os.environ.get('EMAIL_PORT', '25'))
+EMAIL_PORT = int(os.environ.get("EMAIL_PORT", "25"))
# SMTP authentication information for EMAIL_HOST.
# See fig.yml for where these are defined
-EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', 'noreply')
-EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', 'docker')
-EMAIL_USE_TLS = ast.literal_eval(os.environ.get('EMAIL_USE_TLS', 'False'))
-EMAIL_SUBJECT_PREFIX = os.environ.get('EMAIL_SUBJECT_PREFIX', '[QGIS Plugins]')
+EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER", "noreply")
+EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD", "docker")
+EMAIL_USE_TLS = ast.literal_eval(os.environ.get("EMAIL_USE_TLS", "False"))
+EMAIL_SUBJECT_PREFIX = os.environ.get("EMAIL_SUBJECT_PREFIX", "[QGIS Plugins]")
# django uploaded file permission
FILE_UPLOAD_PERMISSIONS = 0o644
REST_FRAMEWORK = {
- 'TEST_REQUEST_DEFAULT_FORMAT': 'json',
+ "TEST_REQUEST_DEFAULT_FORMAT": "json",
}
-
diff --git a/qgis-app/settings_local_vagrant.py b/qgis-app/settings_local_vagrant.py
index 2de2948f..c7bbaead 100644
--- a/qgis-app/settings_local_vagrant.py
+++ b/qgis-app/settings_local_vagrant.py
@@ -1,10 +1,11 @@
import os
+
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
DEBUG = True
-THUMBNAIL_DEBUG = DEBUG # sorl.thumbnail verbose debug
+THUMBNAIL_DEBUG = DEBUG # sorl.thumbnail verbose debug
-ALLOWED_HOSTS = ['*']
+ALLOWED_HOSTS = ["*"]
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
@@ -13,11 +14,11 @@
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
-TIME_ZONE = 'America/Chicago'
+TIME_ZONE = "America/Chicago"
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
SITE_ID = 1
@@ -39,52 +40,52 @@
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
-MEDIA_URL = '/static/'
-MEDIA_URL_FOLDER = '/static/'
+MEDIA_URL = "/static/"
+MEDIA_URL_FOLDER = "/static/"
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = '/admin/'
+ADMIN_MEDIA_PREFIX = "/admin/"
-STATIC_URL='/static_media/'
-STATIC_ROOT=SITE_ROOT + '/static_media/'
+STATIC_URL = "/static_media/"
+STATIC_ROOT = SITE_ROOT + "/static_media/"
# Make this unique, and don't share it with anybody.
-SECRET_KEY = 'y2vu=4qarl)p=g_blq_c4afk!p6u_cor1gy1k@05ro=+tf7+)g'
+SECRET_KEY = "y2vu=4qarl)p=g_blq_c4afk!p6u_cor1gy1k@05ro=+tf7+)g"
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.Loader',
- 'django.template.loaders.app_directories.Loader',
- 'django.template.loaders.eggs.Loader', #Tim: needed on live server for CAB
+ "django.template.loaders.filesystem.Loader",
+ "django.template.loaders.app_directories.Loader",
+ "django.template.loaders.eggs.Loader", # Tim: needed on live server for CAB
)
MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.middleware.cache.UpdateCacheMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
+ "django.middleware.security.SecurityMiddleware",
+ "django.middleware.cache.UpdateCacheMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
# Needed by rpc4django
- 'plugins.middleware.HttpAuthMiddleware',
- 'django.contrib.auth.middleware.RemoteUserMiddleware',
- 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
+ "plugins.middleware.HttpAuthMiddleware",
+ "django.contrib.auth.middleware.RemoteUserMiddleware",
+ "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware",
# Added by Tim for advanced loggin options
- 'django.middleware.cache.FetchFromCacheMiddleware',
- 'middleware.XForwardedForMiddleware',
+ "django.middleware.cache.FetchFromCacheMiddleware",
+ "middleware.XForwardedForMiddleware",
]
-ROOT_URLCONF = 'urls'
+ROOT_URLCONF = "urls"
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
- os.path.join(SITE_ROOT, 'templates'),
+ os.path.join(SITE_ROOT, "templates"),
)
@@ -94,14 +95,14 @@
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': ['templates'],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors' : (
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": ["templates"],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": (
"django.contrib.auth.context_processors.auth",
- 'django.contrib.messages.context_processors.messages',
- 'django.template.context_processors.request',
+ "django.contrib.messages.context_processors.messages",
+ "django.template.context_processors.request",
# ABP: adds DEBUG and BASE_TEMPLATE vars
"qgis_context_processor.additions",
),
@@ -110,89 +111,84 @@
]
-ADMINS = (
- ('Admin', 'admin@email.com'),
-)
+ADMINS = (("Admin", "admin@email.com"),)
MANAGERS = ADMINS
# Tell django which clients may receive debug messages...used by django-debug-toolbar
-INTERNAL_IPS = ('127.0.0.1','')
+INTERNAL_IPS = ("127.0.0.1", "")
# Disable for prod machine
TEMPLATE_DEBUG = DEBUG
-LOGGING_OUTPUT_ENABLED=DEBUG
-LOGGING_LOG_SQL=DEBUG
+LOGGING_OUTPUT_ENABLED = DEBUG
+LOGGING_LOG_SQL = DEBUG
DATABASES = {
- 'default': {
+ "default": {
# Newer django versions may require you to use the postgis backed
-
#'ENGINE': 'django.contrib.gis.db.backends.postgis', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
#'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
- 'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ "ENGINE": "django.db.backends.postgresql_psycopg2", # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
#'NAME': 'qgis-django-plugins.db', # Or path to database file if using sqlite3.
- 'NAME': 'qgis_django', # Or path to database file if using sqlite3.
- 'USER': 'qgis_django', # Not used with sqlite3.
- 'PASSWORD': 'qgis_django', # Not used with sqlite3.
- 'HOST': '127.0.0.1', # Set to empty string for localhost. Not used with sqlite3.
- 'PORT': '5432', # Set to empty string for default. Not used with sqlite3.
+ "NAME": "qgis_django", # Or path to database file if using sqlite3.
+ "USER": "qgis_django", # Not used with sqlite3.
+ "PASSWORD": "qgis_django", # Not used with sqlite3.
+ "HOST": "127.0.0.1", # Set to empty string for localhost. Not used with sqlite3.
+ "PORT": "5432", # Set to empty string for default. Not used with sqlite3.
}
}
-#Tim for google maps in user community page
-#qgis-django.localhost
-GOOGLE_API_KEY='ABQIAAAAyZw9WlHOs4CazzwUByOgZxQok5WFiNcwymBq4ClbhSeQY6fSMhTl0KHT2Donh18dLk3P4AC4ddOarA'
+# Tim for google maps in user community page
+# qgis-django.localhost
+GOOGLE_API_KEY = "ABQIAAAAyZw9WlHOs4CazzwUByOgZxQok5WFiNcwymBq4ClbhSeQY6fSMhTl0KHT2Donh18dLk3P4AC4ddOarA"
-PAGINATION_DEFAULT_PAGINATION=5
-LOGIN_REDIRECT_URL='/'
+PAGINATION_DEFAULT_PAGINATION = 5
+LOGIN_REDIRECT_URL = "/"
SERVE_STATIC_MEDIA = DEBUG
# TIM: Place where search indexes are stored for snippets - should be non web accessible
HAYSTACK_CONNECTIONS = {
- 'default': {
- 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
- 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
+ "default": {
+ "ENGINE": "haystack.backends.whoosh_backend.WhooshEngine",
+ "PATH": os.path.join(os.path.dirname(__file__), "whoosh_index"),
},
}
# Tim Email settings
-EMAIL_HOST = 'localhost'
-#EMAIL_PORT =
-DEFAULT_FROM_EMAIL = 'noreply@qgis.org'
+EMAIL_HOST = "localhost"
+# EMAIL_PORT =
+DEFAULT_FROM_EMAIL = "noreply@qgis.org"
INSTALLED_APPS = [
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.messages',
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.sites",
+ "django.contrib.messages",
# Uncomment the next line to enable the admin:
- 'django.contrib.admin',
+ "django.contrib.admin",
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
- 'django.contrib.staticfiles',
- 'django.contrib.flatpages',
-
+ "django.contrib.staticfiles",
+ "django.contrib.flatpages",
# ABP:
- 'plugins',
- 'django.contrib.humanize',
- 'django.contrib.syndication',
- 'bootstrap_pagination',
- 'sortable_listview',
- 'lib', # Container for small tags and functions
- 'sorl.thumbnail',
- 'djangoratings',
-
- 'taggit',
- 'taggit_autosuggest',
- 'taggit_templatetags',
- 'haystack',
- 'simplemenu',
- 'tinymce',
- 'rpc4django',
+ "plugins",
+ "django.contrib.humanize",
+ "django.contrib.syndication",
+ "bootstrap_pagination",
+ "sortable_listview",
+ "lib", # Container for small tags and functions
+ "sorl.thumbnail",
+ "djangoratings",
+ "taggit",
+ "taggit_autosuggest",
+ "taggit_templatetags",
+ "haystack",
+ "simplemenu",
+ "tinymce",
+ "rpc4django",
]
-PLUGIN_MAX_UPLOAD_SIZE=1024*1024*20
+PLUGIN_MAX_UPLOAD_SIZE = 1024 * 1024 * 20
if DEBUG:
- INSTALLED_APPS.append('django_extensions')
\ No newline at end of file
+ INSTALLED_APPS.append("django_extensions")
diff --git a/qgis-app/styles/admin.py b/qgis-app/styles/admin.py
index 97472439..f86f7b52 100644
--- a/qgis-app/styles/admin.py
+++ b/qgis-app/styles/admin.py
@@ -1,20 +1,23 @@
from django.contrib import admin
-from .models import Style, StyleType, Review
+
+from .models import Review, Style, StyleType
class StyleTypeAdmin(admin.ModelAdmin):
- list_display = ('name', 'description', 'order')
- list_editable = ('order',)
+ list_display = ("name", "description", "order")
+ list_editable = ("order",)
class StyleAdmin(admin.ModelAdmin):
- list_display = ('name',
- 'description',
- 'creator',
- 'upload_date',
- 'modified_date',
- 'style_type')
- search_fields = ('name', 'description')
+ list_display = (
+ "name",
+ "description",
+ "creator",
+ "upload_date",
+ "modified_date",
+ "style_type",
+ )
+ search_fields = ("name", "description")
admin.site.register(Style, StyleAdmin)
diff --git a/qgis-app/styles/apps.py b/qgis-app/styles/apps.py
index 28b7d778..5d8f8038 100644
--- a/qgis-app/styles/apps.py
+++ b/qgis-app/styles/apps.py
@@ -2,4 +2,4 @@
class StylesConfig(AppConfig):
- name = 'styles'
+ name = "styles"
diff --git a/qgis-app/styles/file_handler.py b/qgis-app/styles/file_handler.py
index 2a296634..e35e855f 100644
--- a/qgis-app/styles/file_handler.py
+++ b/qgis-app/styles/file_handler.py
@@ -11,15 +11,17 @@ def _check_name_type_attribute(element):
"""
Check if element has name and type attribute.
"""
- style_name = element.get('name')
+ style_name = element.get("name")
if not style_name:
- raise ValidationError(_('Undefined style name. '
- 'Please register your style type.'))
- if element.tag == 'symbol':
- style_type = element.get('type')
+ raise ValidationError(
+ _("Undefined style name. " "Please register your style type.")
+ )
+ if element.tag == "symbol":
+ style_type = element.get("type")
if not style_type:
- raise ValidationError(_('Undefined style type. '
- 'Please register your style type.'))
+ raise ValidationError(
+ _("Undefined style type. " "Please register your style type.")
+ )
def validator(xmlfile):
@@ -45,26 +47,32 @@ def validator(xmlfile):
try:
tree = ET.parse(xmlfile)
except ET.ParseError:
- raise ValidationError(_('Cannot parse the style file. '
- 'Please ensure your file is correct.'))
+ raise ValidationError(
+ _("Cannot parse the style file. " "Please ensure your file is correct.")
+ )
root = tree.getroot()
- if not root or not root.tag == 'qgis_style':
- raise ValidationError(_('Invalid root tag of style file. '
- 'Please ensure your file is correct.'))
+ if not root or not root.tag == "qgis_style":
+ raise ValidationError(
+ _("Invalid root tag of style file. " "Please ensure your file is correct.")
+ )
# find child elements
- symbol = root.find('./symbols/symbol')
- colorramp = root.find('./colorramps/colorramp')
- labelsetting = root.find('./labelsettings/labelsetting')
- legendpatchshape = root.find('./legendpatchshapes/legendpatchshape')
- symbol3d = root.find('./symbols3d/symbol3d')
- textformat = root.find('./textformats/textformat')
- if not symbol and not colorramp \
- and not labelsetting \
- and not legendpatchshape \
- and not symbol3d \
- and not textformat:
- raise ValidationError(_('Undefined style type. '
- 'Please register your style type.'))
+ symbol = root.find("./symbols/symbol")
+ colorramp = root.find("./colorramps/colorramp")
+ labelsetting = root.find("./labelsettings/labelsetting")
+ legendpatchshape = root.find("./legendpatchshapes/legendpatchshape")
+ symbol3d = root.find("./symbols3d/symbol3d")
+ textformat = root.find("./textformats/textformat")
+ if (
+ not symbol
+ and not colorramp
+ and not labelsetting
+ and not legendpatchshape
+ and not symbol3d
+ and not textformat
+ ):
+ raise ValidationError(
+ _("Undefined style type. " "Please register your style type.")
+ )
if symbol:
_check_name_type_attribute(symbol)
elif colorramp:
@@ -98,34 +106,29 @@ def read_xml_style(xmlfile):
try:
tree = ET.parse(xmlfile)
except ET.ParseError:
- raise ValidationError(_('Cannot parse the style file. '
- 'Please ensure your file is correct.'))
+ raise ValidationError(
+ _("Cannot parse the style file. " "Please ensure your file is correct.")
+ )
root = tree.getroot()
# find child elements
- symbol = root.find('./symbols/symbol')
- colorramp = root.find('./colorramps/colorramp')
- labelsetting = root.find('./labelsettings/labelsetting')
- legendpatchshape = root.find('./legendpatchshapes/legendpatchshape')
- symbol3d = root.find('./symbols3d/symbol3d')
- textformat = root.find('./textformats/textformat')
+ symbol = root.find("./symbols/symbol")
+ colorramp = root.find("./colorramps/colorramp")
+ labelsetting = root.find("./labelsettings/labelsetting")
+ legendpatchshape = root.find("./legendpatchshapes/legendpatchshape")
+ symbol3d = root.find("./symbols3d/symbol3d")
+ textformat = root.find("./textformats/textformat")
if symbol:
- return {'name': symbol.get('name'),
- 'type': symbol.get('type')}
+ return {"name": symbol.get("name"), "type": symbol.get("type")}
elif colorramp:
- return {'name': colorramp.get('name'),
- 'type': 'colorramp'}
+ return {"name": colorramp.get("name"), "type": "colorramp"}
elif labelsetting:
- return {'name': labelsetting.get('name'),
- 'type': 'labelsetting'}
+ return {"name": labelsetting.get("name"), "type": "labelsetting"}
elif legendpatchshape:
- return {'name': legendpatchshape.get('name'),
- 'type': 'legendpatchshape'}
+ return {"name": legendpatchshape.get("name"), "type": "legendpatchshape"}
elif symbol3d:
- return {'name': symbol3d.get('name'),
- 'type': 'symbol3d'}
+ return {"name": symbol3d.get("name"), "type": "symbol3d"}
elif textformat:
- return {'name': textformat.get('name'),
- 'type': 'textformat'}
+ return {"name": textformat.get("name"), "type": "textformat"}
else:
- return {'name': None, 'type': None}
+ return {"name": None, "type": None}
diff --git a/qgis-app/styles/forms.py b/qgis-app/styles/forms.py
index cb2ca90d..3fe24047 100644
--- a/qgis-app/styles/forms.py
+++ b/qgis-app/styles/forms.py
@@ -1,15 +1,19 @@
-from django.utils.translation import ugettext_lazy as _
from django import forms
from django.core.exceptions import ValidationError
-
-from styles.models import Style
+from django.utils.translation import ugettext_lazy as _
from styles.file_handler import validator
+from styles.models import Style
class ResourceFormMixin(forms.ModelForm):
class Meta:
model = Style
- fields = ['file', 'thumbnail_image', 'name', 'description', ]
+ fields = [
+ "file",
+ "thumbnail_image",
+ "name",
+ "description",
+ ]
class UploadForm(forms.ModelForm):
@@ -19,20 +23,25 @@ class UploadForm(forms.ModelForm):
class Meta:
model = Style
- fields = ['file', 'thumbnail_image', 'description', ]
+ fields = [
+ "file",
+ "thumbnail_image",
+ "description",
+ ]
def clean_file(self):
"""
Cleaning file field data.
"""
- xml_file = self.cleaned_data['file']
+ xml_file = self.cleaned_data["file"]
if xml_file:
style = validator(xml_file.file)
if not style:
- raise ValidationError(_('Undefined style type. '
- 'Please register your style type.'))
+ raise ValidationError(
+ _("Undefined style type. " "Please register your style type.")
+ )
return xml_file
@@ -45,13 +54,19 @@ class StyleReviewForm(forms.Form):
Style Review Form.
"""
- CHOICES = [('approve', 'Approve'), ('reject', 'Reject')]
- approval = forms.ChoiceField(required=True, choices=CHOICES,
- widget=forms.RadioSelect, initial='approve')
- comment = forms.CharField(widget=forms.Textarea(
- attrs={'placeholder': 'Please provide clear feedback '
- 'if you decided to not approve this style.',
- 'rows': "5"}))
+ CHOICES = [("approve", "Approve"), ("reject", "Reject")]
+ approval = forms.ChoiceField(
+ required=True, choices=CHOICES, widget=forms.RadioSelect, initial="approve"
+ )
+ comment = forms.CharField(
+ widget=forms.Textarea(
+ attrs={
+ "placeholder": "Please provide clear feedback "
+ "if you decided to not approve this style.",
+ "rows": "5",
+ }
+ )
+ )
class StyleSearchForm(forms.Form):
@@ -59,5 +74,9 @@ class StyleSearchForm(forms.Form):
Search Form
"""
- q = forms.CharField(required=False, widget=forms.TextInput(
- attrs={'class': 'search-query', 'placeholder': 'Search'}))
+ q = forms.CharField(
+ required=False,
+ widget=forms.TextInput(
+ attrs={"class": "search-query", "placeholder": "Search"}
+ ),
+ )
diff --git a/qgis-app/styles/migrations/0001_initial.py b/qgis-app/styles/migrations/0001_initial.py
index 913ba36d..6259b61e 100644
--- a/qgis-app/styles/migrations/0001_initial.py
+++ b/qgis-app/styles/migrations/0001_initial.py
@@ -1,10 +1,10 @@
# Generated by Django 2.2 on 2020-11-07 14:15
-from django.conf import settings
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -17,35 +17,170 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
- name='StyleType',
+ name="StyleType",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('symbol_type', models.CharField(help_text='This will be used to identify the type of a style. The value will be extracted automatically from the uploaded QGIS style XML file.', max_length=256, unique=True, verbose_name='Symbol type')),
- ('name', models.CharField(help_text='Default to the title case string of symbol_type. e.g. line would become Line.', max_length=256, unique=True, verbose_name='Name')),
- ('description', models.TextField(blank=True, help_text='A short description of this style type.', max_length=1000, null=True, verbose_name='Description')),
- ('icon', models.ImageField(blank=True, help_text='Please ensure the icon file is 500x500 px and in PNG format.', null=True, upload_to='styles/%Y', verbose_name='Icon')),
- ('order', models.IntegerField(default=0, help_text='Order value for custom ordering.', verbose_name='Order')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "symbol_type",
+ models.CharField(
+ help_text="This will be used to identify the type of a style. The value will be extracted automatically from the uploaded QGIS style XML file.",
+ max_length=256,
+ unique=True,
+ verbose_name="Symbol type",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ help_text="Default to the title case string of symbol_type. e.g. line would become Line.",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
+ ),
+ (
+ "description",
+ models.TextField(
+ blank=True,
+ help_text="A short description of this style type.",
+ max_length=1000,
+ null=True,
+ verbose_name="Description",
+ ),
+ ),
+ (
+ "icon",
+ models.ImageField(
+ blank=True,
+ help_text="Please ensure the icon file is 500x500 px and in PNG format.",
+ null=True,
+ upload_to="styles/%Y",
+ verbose_name="Icon",
+ ),
+ ),
+ (
+ "order",
+ models.IntegerField(
+ default=0,
+ help_text="Order value for custom ordering.",
+ verbose_name="Order",
+ ),
+ ),
],
options={
- 'ordering': ('order',),
+ "ordering": ("order",),
},
),
migrations.CreateModel(
- name='Style',
+ name="Style",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('upload_date', models.DateTimeField(auto_now_add=True, help_text='The upload date. Automatically added on file upload.', verbose_name='Uploaded on')),
- ('name', models.CharField(help_text='A unique name for this style. This will be initially automatically taken from the uploaded XML file, but may need manual revision if the name is not unique.', max_length=256, unique=True, verbose_name='Name')),
- ('description', models.TextField(help_text='A description of this style.', max_length=5000, verbose_name='Description')),
- ('thumbnail_image', models.ImageField(blank=True, help_text='Please upload an image that represents this style. The image should be square when uploaded.', null=True, upload_to='styles/%Y', verbose_name='Thumbnail')),
- ('xml_file', models.FileField(help_text='A QGIS style file in XML format.', upload_to='styles/%Y', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['xml'])], verbose_name='Style file')),
- ('download_count', models.IntegerField(default=0, editable=False, help_text='The number of times this style has been downloaded. This is updated automatically.', verbose_name='Downloads')),
- ('approved', models.BooleanField(db_index=True, default=False, help_text='Set to True if you wish to approve this style.', verbose_name='Approved')),
- ('creator', models.ForeignKey(help_text='The user who uploaded this style.', on_delete=django.db.models.deletion.CASCADE, related_name='styles_created_by', to=settings.AUTH_USER_MODEL, verbose_name='Created by')),
- ('style_type', models.ForeignKey(blank=True, help_text='The type of this style, this will automatically be read from the XML file.', null=True, on_delete=django.db.models.deletion.CASCADE, to='styles.StyleType', verbose_name='Type')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "upload_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Uploaded on",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(
+ help_text="A unique name for this style. This will be initially automatically taken from the uploaded XML file, but may need manual revision if the name is not unique.",
+ max_length=256,
+ unique=True,
+ verbose_name="Name",
+ ),
+ ),
+ (
+ "description",
+ models.TextField(
+ help_text="A description of this style.",
+ max_length=5000,
+ verbose_name="Description",
+ ),
+ ),
+ (
+ "thumbnail_image",
+ models.ImageField(
+ blank=True,
+ help_text="Please upload an image that represents this style. The image should be square when uploaded.",
+ null=True,
+ upload_to="styles/%Y",
+ verbose_name="Thumbnail",
+ ),
+ ),
+ (
+ "xml_file",
+ models.FileField(
+ help_text="A QGIS style file in XML format.",
+ upload_to="styles/%Y",
+ validators=[
+ django.core.validators.FileExtensionValidator(
+ allowed_extensions=["xml"]
+ )
+ ],
+ verbose_name="Style file",
+ ),
+ ),
+ (
+ "download_count",
+ models.IntegerField(
+ default=0,
+ editable=False,
+ help_text="The number of times this style has been downloaded. This is updated automatically.",
+ verbose_name="Downloads",
+ ),
+ ),
+ (
+ "approved",
+ models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you wish to approve this style.",
+ verbose_name="Approved",
+ ),
+ ),
+ (
+ "creator",
+ models.ForeignKey(
+ help_text="The user who uploaded this style.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="styles_created_by",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
+ ),
+ (
+ "style_type",
+ models.ForeignKey(
+ blank=True,
+ help_text="The type of this style, this will automatically be read from the XML file.",
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="styles.StyleType",
+ verbose_name="Type",
+ ),
+ ),
],
managers=[
- ('unapproved_objects', django.db.models.manager.Manager()),
+ ("unapproved_objects", django.db.models.manager.Manager()),
],
),
]
diff --git a/qgis-app/styles/migrations/0002_auto_20201108_0521.py b/qgis-app/styles/migrations/0002_auto_20201108_0521.py
index 1d6a92fd..f593dfac 100644
--- a/qgis-app/styles/migrations/0002_auto_20201108_0521.py
+++ b/qgis-app/styles/migrations/0002_auto_20201108_0521.py
@@ -1,30 +1,63 @@
# Generated by Django 2.2 on 2020-11-08 05:21
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('styles', '0001_initial'),
+ ("styles", "0001_initial"),
]
operations = [
migrations.AlterModelManagers(
- name='style',
- managers=[
- ],
+ name="style",
+ managers=[],
),
migrations.CreateModel(
- name='StyleReview',
+ name="StyleReview",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('review_date', models.DateTimeField(auto_now_add=True, help_text='The review date. Automatically added on style review.', verbose_name='Reviewed on')),
- ('reviewer', models.ForeignKey(help_text='The user who reviewed this style.', on_delete=django.db.models.deletion.CASCADE, related_name='styles_reviewed_by', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by')),
- ('style', models.ForeignKey(blank=True, help_text='The type of this style, this will automatically be read from the XML file.', null=True, on_delete=django.db.models.deletion.CASCADE, to='styles.Style', verbose_name='Style')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "review_date",
+ models.DateTimeField(
+ auto_now_add=True,
+ help_text="The review date. Automatically added on style review.",
+ verbose_name="Reviewed on",
+ ),
+ ),
+ (
+ "reviewer",
+ models.ForeignKey(
+ help_text="The user who reviewed this style.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="styles_reviewed_by",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
+ ),
+ (
+ "style",
+ models.ForeignKey(
+ blank=True,
+ help_text="The type of this style, this will automatically be read from the XML file.",
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="styles.Style",
+ verbose_name="Style",
+ ),
+ ),
],
),
]
diff --git a/qgis-app/styles/migrations/0003_stylereview_comment.py b/qgis-app/styles/migrations/0003_stylereview_comment.py
index 0ee1df98..a42bf9bb 100644
--- a/qgis-app/styles/migrations/0003_stylereview_comment.py
+++ b/qgis-app/styles/migrations/0003_stylereview_comment.py
@@ -6,13 +6,19 @@
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0002_auto_20201108_0521'),
+ ("styles", "0002_auto_20201108_0521"),
]
operations = [
migrations.AddField(
- model_name='stylereview',
- name='comment',
- field=models.TextField(blank=True, help_text='A review comment. Please write your review.', max_length=1000, null=True, verbose_name='Comment'),
+ model_name="stylereview",
+ name="comment",
+ field=models.TextField(
+ blank=True,
+ help_text="A review comment. Please write your review.",
+ max_length=1000,
+ null=True,
+ verbose_name="Comment",
+ ),
),
]
diff --git a/qgis-app/styles/migrations/0004_auto_20201108_0742.py b/qgis-app/styles/migrations/0004_auto_20201108_0742.py
index ee3a5ac1..80f17eba 100644
--- a/qgis-app/styles/migrations/0004_auto_20201108_0742.py
+++ b/qgis-app/styles/migrations/0004_auto_20201108_0742.py
@@ -6,12 +6,12 @@
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0003_stylereview_comment'),
+ ("styles", "0003_stylereview_comment"),
]
operations = [
migrations.AlterModelOptions(
- name='stylereview',
- options={'ordering': ['review_date']},
+ name="stylereview",
+ options={"ordering": ["review_date"]},
),
]
diff --git a/qgis-app/styles/migrations/0005_style_modified_date.py b/qgis-app/styles/migrations/0005_style_modified_date.py
index 4088f46d..826b2506 100644
--- a/qgis-app/styles/migrations/0005_style_modified_date.py
+++ b/qgis-app/styles/migrations/0005_style_modified_date.py
@@ -1,20 +1,26 @@
# Generated by Django 2.2 on 2020-11-08 11:00
import datetime
+
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0004_auto_20201108_0742'),
+ ("styles", "0004_auto_20201108_0742"),
]
operations = [
migrations.AddField(
- model_name='style',
- name='modified_date',
- field=models.DateTimeField(default=datetime.datetime(2020, 11, 8, 11, 0, 41, 965734), editable=False, help_text='The upload date. Automatically added on file upload.', verbose_name='Modified on'),
+ model_name="style",
+ name="modified_date",
+ field=models.DateTimeField(
+ default=datetime.datetime(2020, 11, 8, 11, 0, 41, 965734),
+ editable=False,
+ help_text="The upload date. Automatically added on file upload.",
+ verbose_name="Modified on",
+ ),
preserve_default=False,
),
]
diff --git a/qgis-app/styles/migrations/0006_stylereview_require_action.py b/qgis-app/styles/migrations/0006_stylereview_require_action.py
index 59aba3da..38ea66d7 100644
--- a/qgis-app/styles/migrations/0006_stylereview_require_action.py
+++ b/qgis-app/styles/migrations/0006_stylereview_require_action.py
@@ -6,13 +6,18 @@
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0005_style_modified_date'),
+ ("styles", "0005_style_modified_date"),
]
operations = [
migrations.AddField(
- model_name='stylereview',
- name='require_action',
- field=models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update its style.', verbose_name='Requires Action'),
+ model_name="stylereview",
+ name="require_action",
+ field=models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update its style.",
+ verbose_name="Requires Action",
+ ),
),
]
diff --git a/qgis-app/styles/migrations/0007_auto_20201109_0112.py b/qgis-app/styles/migrations/0007_auto_20201109_0112.py
index 07cca35e..005d59d8 100644
--- a/qgis-app/styles/migrations/0007_auto_20201109_0112.py
+++ b/qgis-app/styles/migrations/0007_auto_20201109_0112.py
@@ -6,17 +6,22 @@
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0006_stylereview_require_action'),
+ ("styles", "0006_stylereview_require_action"),
]
operations = [
migrations.RemoveField(
- model_name='stylereview',
- name='require_action',
+ model_name="stylereview",
+ name="require_action",
),
migrations.AddField(
- model_name='style',
- name='require_action',
- field=models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update its style.', verbose_name='Requires Action'),
+ model_name="style",
+ name="require_action",
+ field=models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update its style.",
+ verbose_name="Requires Action",
+ ),
),
]
diff --git a/qgis-app/styles/migrations/0008_auto_20201215_2124.py b/qgis-app/styles/migrations/0008_auto_20201215_2124.py
index 5983fe5d..2ce1fbb4 100644
--- a/qgis-app/styles/migrations/0008_auto_20201215_2124.py
+++ b/qgis-app/styles/migrations/0008_auto_20201215_2124.py
@@ -1,45 +1,74 @@
# Generated by Django 2.2.13 on 2020-12-15 21:24
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0007_auto_20201109_0112'),
+ ("styles", "0007_auto_20201109_0112"),
]
operations = [
migrations.AlterField(
- model_name='style',
- name='approved',
- field=models.BooleanField(db_index=True, default=False, help_text='Set to True if you wish to approve this resource.', verbose_name='Approved'),
+ model_name="style",
+ name="approved",
+ field=models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you wish to approve this resource.",
+ verbose_name="Approved",
+ ),
),
migrations.AlterField(
- model_name='style',
- name='creator',
- field=models.ForeignKey(help_text='The user who uploaded this resource.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Created by'),
+ model_name="style",
+ name="creator",
+ field=models.ForeignKey(
+ help_text="The user who uploaded this resource.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Created by",
+ ),
),
migrations.AlterField(
- model_name='style',
- name='download_count',
- field=models.IntegerField(default=0, editable=False, help_text='The number of times this resource has been downloaded. This is updated automatically.', verbose_name='Downloads'),
+ model_name="style",
+ name="download_count",
+ field=models.IntegerField(
+ default=0,
+ editable=False,
+ help_text="The number of times this resource has been downloaded. This is updated automatically.",
+ verbose_name="Downloads",
+ ),
),
migrations.AlterField(
- model_name='style',
- name='require_action',
- field=models.BooleanField(db_index=True, default=False, help_text='Set to True if you require creator to update the resource.', verbose_name='Requires Action'),
+ model_name="style",
+ name="require_action",
+ field=models.BooleanField(
+ db_index=True,
+ default=False,
+ help_text="Set to True if you require creator to update the resource.",
+ verbose_name="Requires Action",
+ ),
),
migrations.AlterField(
- model_name='stylereview',
- name='review_date',
- field=models.DateTimeField(auto_now_add=True, help_text='The review date. Automatically added on review resource.', verbose_name='Reviewed on'),
+ model_name="stylereview",
+ name="review_date",
+ field=models.DateTimeField(
+ auto_now_add=True,
+ help_text="The review date. Automatically added on review resource.",
+ verbose_name="Reviewed on",
+ ),
),
migrations.AlterField(
- model_name='stylereview',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this GeoPackage.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="stylereview",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/styles/migrations/0009_auto_20210121_0227.py b/qgis-app/styles/migrations/0009_auto_20210121_0227.py
index a7d88ada..a878a4fb 100644
--- a/qgis-app/styles/migrations/0009_auto_20210121_0227.py
+++ b/qgis-app/styles/migrations/0009_auto_20210121_0227.py
@@ -1,20 +1,26 @@
# Generated by Django 2.2.13 on 2021-01-21 02:27
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0008_auto_20201215_2124'),
+ ("styles", "0008_auto_20201215_2124"),
]
operations = [
migrations.AlterField(
- model_name='stylereview',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this GeoPackage.', on_delete=django.db.models.deletion.CASCADE, related_name='styles_stylereview_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="stylereview",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this GeoPackage.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="styles_stylereview_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/styles/migrations/0010_rename_Review_model_and_file_field.py b/qgis-app/styles/migrations/0010_rename_Review_model_and_file_field.py
index 48304985..e50a0a4d 100644
--- a/qgis-app/styles/migrations/0010_rename_Review_model_and_file_field.py
+++ b/qgis-app/styles/migrations/0010_rename_Review_model_and_file_field.py
@@ -1,42 +1,38 @@
# Custom migaration, rename Review class, rename file field, add related_name
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('styles', '0009_auto_20210121_0227'),
+ ("styles", "0009_auto_20210121_0227"),
]
operations = [
migrations.RenameField(
- model_name='style',
- old_name='xml_file',
- new_name='file',
+ model_name="style",
+ old_name="xml_file",
+ new_name="file",
),
-
migrations.RenameField(
- model_name='stylereview',
- old_name='style',
- new_name='resource',
+ model_name="stylereview",
+ old_name="style",
+ new_name="resource",
),
-
- migrations.RenameModel(
- old_name='StyleReview',
- new_name='Review'
- ),
-
+ migrations.RenameModel(old_name="StyleReview", new_name="Review"),
migrations.AlterField(
- model_name='review',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this Style.',
- on_delete=django.db.models.deletion.CASCADE,
- related_name='styles_review_related', to=settings.AUTH_USER_MODEL,
- verbose_name='Reviewed by'),
+ model_name="review",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this Style.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="styles_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
-
]
diff --git a/qgis-app/styles/migrations/0011_auto_20210203_2142.py b/qgis-app/styles/migrations/0011_auto_20210203_2142.py
index 41b19d16..a2cc897e 100644
--- a/qgis-app/styles/migrations/0011_auto_20210203_2142.py
+++ b/qgis-app/styles/migrations/0011_auto_20210203_2142.py
@@ -1,25 +1,38 @@
# Generated by Django 2.2.13 on 2021-02-03 21:42
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0010_rename_Review_model_and_file_field'),
+ ("styles", "0010_rename_Review_model_and_file_field"),
]
operations = [
migrations.AlterField(
- model_name='review',
- name='resource',
- field=models.ForeignKey(blank=True, help_text='The reviewed Style.', null=True, on_delete=django.db.models.deletion.CASCADE, to='styles.Style', verbose_name='Style'),
+ model_name="review",
+ name="resource",
+ field=models.ForeignKey(
+ blank=True,
+ help_text="The reviewed Style.",
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="styles.Style",
+ verbose_name="Style",
+ ),
),
migrations.AlterField(
- model_name='review',
- name='reviewer',
- field=models.ForeignKey(help_text='The user who reviewed this %(app_label)s.', on_delete=django.db.models.deletion.CASCADE, related_name='styles_review_related', to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by'),
+ model_name="review",
+ name="reviewer",
+ field=models.ForeignKey(
+ help_text="The user who reviewed this %(app_label)s.",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="styles_review_related",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="Reviewed by",
+ ),
),
]
diff --git a/qgis-app/styles/migrations/0012_add_uuid_field.py b/qgis-app/styles/migrations/0012_add_uuid_field.py
index 99338aca..50bb158c 100644
--- a/qgis-app/styles/migrations/0012_add_uuid_field.py
+++ b/qgis-app/styles/migrations/0012_add_uuid_field.py
@@ -1,19 +1,20 @@
# Generated by Django 2.2.13 on 2021-02-10 19:47
-from django.db import migrations, models
import uuid
+from django.db import migrations, models
+
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0011_auto_20210203_2142'),
+ ("styles", "0011_auto_20210203_2142"),
]
operations = [
migrations.AddField(
- model_name='style',
- name='uuid',
+ model_name="style",
+ name="uuid",
field=models.UUIDField(default=uuid.uuid4, null=True),
),
]
diff --git a/qgis-app/styles/migrations/0013_populate_uuid_value.py b/qgis-app/styles/migrations/0013_populate_uuid_value.py
index 3fe54ee5..de04f472 100644
--- a/qgis-app/styles/migrations/0013_populate_uuid_value.py
+++ b/qgis-app/styles/migrations/0013_populate_uuid_value.py
@@ -1,20 +1,21 @@
# Generated by Django 2.2.13 on 2021-02-10 19:47
-from django.db import migrations
import uuid
+from django.db import migrations
+
def gen_uuid(apps, schema_editor):
- Style = apps.get_model('styles', 'Style')
+ Style = apps.get_model("styles", "Style")
for row in Style.objects.all():
row.uuid = uuid.uuid4()
- row.save(update_fields=['uuid'])
+ row.save(update_fields=["uuid"])
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0012_add_uuid_field'),
+ ("styles", "0012_add_uuid_field"),
]
operations = [
diff --git a/qgis-app/styles/migrations/0014_remove_uuid_null.py b/qgis-app/styles/migrations/0014_remove_uuid_null.py
index 2ce77a9d..10293193 100644
--- a/qgis-app/styles/migrations/0014_remove_uuid_null.py
+++ b/qgis-app/styles/migrations/0014_remove_uuid_null.py
@@ -1,19 +1,20 @@
# Generated by Django 2.2.13 on 2021-02-10 19:48
-from django.db import migrations, models
import uuid
+from django.db import migrations, models
+
class Migration(migrations.Migration):
dependencies = [
- ('styles', '0013_populate_uuid_value'),
+ ("styles", "0013_populate_uuid_value"),
]
operations = [
migrations.AlterField(
- model_name='style',
- name='uuid',
+ model_name="style",
+ name="uuid",
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
diff --git a/qgis-app/styles/models.py b/qgis-app/styles/models.py
index 2c19e348..f1e4f753 100644
--- a/qgis-app/styles/models.py
+++ b/qgis-app/styles/models.py
@@ -1,13 +1,11 @@
-from django.db import models
+from base.models.processing_models import Resource, ResourceReview
from django.conf import settings
from django.core.validators import FileExtensionValidator
+from django.db import models
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
-from base.models.processing_models import Resource, ResourceReview
-
-STYLES_STORAGE_PATH = getattr(settings,
- 'PLUGINS_STORAGE_PATH', 'styles/%Y')
+STYLES_STORAGE_PATH = getattr(settings, "PLUGINS_STORAGE_PATH", "styles/%Y")
class StyleType(models.Model):
@@ -19,42 +17,48 @@ class StyleType(models.Model):
# symbol_type e.g. "line"
# name e.g. "Line"
symbol_type = models.CharField(
- _('Symbol type'),
- help_text=_('This will be used to identify the type of a style. '
- 'The value will be extracted automatically from '
- 'the uploaded QGIS style XML file.'),
+ _("Symbol type"),
+ help_text=_(
+ "This will be used to identify the type of a style. "
+ "The value will be extracted automatically from "
+ "the uploaded QGIS style XML file."
+ ),
max_length=256,
- unique=True)
+ unique=True,
+ )
name = models.CharField(
- _('Name'),
- help_text=_('Default to the title case string of symbol_type. '
- 'e.g. line would become Line.'),
+ _("Name"),
+ help_text=_(
+ "Default to the title case string of symbol_type. "
+ "e.g. line would become Line."
+ ),
max_length=256,
- unique=True)
+ unique=True,
+ )
description = models.TextField(
- _('Description'),
- help_text=_('A short description of this style type.'),
+ _("Description"),
+ help_text=_("A short description of this style type."),
max_length=1000,
blank=True,
- null=True)
+ null=True,
+ )
# icon image
icon = models.ImageField(
- _('Icon'),
- help_text=_('Please ensure the icon file is 500x500 px '
- 'and in PNG format.'),
+ _("Icon"),
+ help_text=_("Please ensure the icon file is 500x500 px " "and in PNG format."),
upload_to=STYLES_STORAGE_PATH,
blank=True,
- null=True)
+ null=True,
+ )
# Ordering for StyleType instance
order = models.IntegerField(
- _('Order'),
- help_text=_('Order value for custom ordering.'),
- default=0)
+ _("Order"), help_text=_("Order value for custom ordering."), default=0
+ )
class Meta:
- ordering = ('order',)
+ ordering = ("order",)
def __unicode__(self):
return "%s" % (self.name)
@@ -71,47 +75,57 @@ class Style(Resource):
"""
# style type
- style_type = models.ForeignKey(StyleType,
- verbose_name=_('Type'),
- help_text=_('The type of this style, this will automatically be read '
- 'from the XML file.'),
+ style_type = models.ForeignKey(
+ StyleType,
+ verbose_name=_("Type"),
+ help_text=_(
+ "The type of this style, this will automatically be read "
+ "from the XML file."
+ ),
blank=True,
null=True,
on_delete=models.CASCADE,
- db_index=True)
+ db_index=True,
+ )
# name and desc
- name = models.CharField(_('Name'),
- help_text=_('A unique name for this style. This will be initially '
- 'automatically taken from the uploaded XML file, but may '
- 'need manual revision if the name is not unique.'),
+ name = models.CharField(
+ _("Name"),
+ help_text=_(
+ "A unique name for this style. This will be initially "
+ "automatically taken from the uploaded XML file, but may "
+ "need manual revision if the name is not unique."
+ ),
max_length=256,
- unique=True)
+ unique=True,
+ )
description = models.TextField(
- _('Description'),
- help_text=_('A description of this style.'),
- max_length=5000
+ _("Description"), help_text=_("A description of this style."), max_length=5000
)
# thumbnail
thumbnail_image = models.ImageField(
- _('Thumbnail'),
- help_text=_('Please upload an image that represents this style. '
- 'The image should be square when uploaded.'),
+ _("Thumbnail"),
+ help_text=_(
+ "Please upload an image that represents this style. "
+ "The image should be square when uploaded."
+ ),
blank=True,
null=True,
- upload_to=STYLES_STORAGE_PATH)
+ upload_to=STYLES_STORAGE_PATH,
+ )
# file
file = models.FileField(
- _('Style file'),
- help_text=_('A QGIS style file in XML format.'),
+ _("Style file"),
+ help_text=_("A QGIS style file in XML format."),
upload_to=STYLES_STORAGE_PATH,
- validators=[FileExtensionValidator(allowed_extensions=['xml'])],
- null=False)
+ validators=[FileExtensionValidator(allowed_extensions=["xml"])],
+ null=False,
+ )
def get_absolute_url(self):
- return reverse('style_detail', args=(self.id,))
+ return reverse("style_detail", args=(self.id,))
@property
def get_style_type(self):
@@ -123,8 +137,9 @@ class Review(ResourceReview):
# style
resource = models.ForeignKey(
Style,
- verbose_name=_('Style'),
- help_text=_('The reviewed Style.'),
+ verbose_name=_("Style"),
+ help_text=_("The reviewed Style."),
blank=True,
null=True,
- on_delete=models.CASCADE)
+ on_delete=models.CASCADE,
+ )
diff --git a/qgis-app/styles/templates/styles/style_detail.html b/qgis-app/styles/templates/styles/style_detail.html
index 601181b4..f90f71e8 100644
--- a/qgis-app/styles/templates/styles/style_detail.html
+++ b/qgis-app/styles/templates/styles/style_detail.html
@@ -59,4 +59,4 @@ {{ style_detail.name|title }}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/qgis-app/styles/templates/styles/style_form.html b/qgis-app/styles/templates/styles/style_form.html
index bdd6e869..dfcda750 100644
--- a/qgis-app/styles/templates/styles/style_form.html
+++ b/qgis-app/styles/templates/styles/style_form.html
@@ -27,4 +27,4 @@ {% trans "Upload Style" %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/qgis-app/styles/templates/styles/style_list.html b/qgis-app/styles/templates/styles/style_list.html
index 2bccb95e..da528ade 100644
--- a/qgis-app/styles/templates/styles/style_list.html
+++ b/qgis-app/styles/templates/styles/style_list.html
@@ -136,4 +136,3 @@ {% if title %}{{title}}{% else %}{% trans "All Styles" %}{% endif %}
})
{% endblock %}
-
diff --git a/qgis-app/styles/templates/styles/style_review.html b/qgis-app/styles/templates/styles/style_review.html
index 4c8f38e6..cfc38d27 100644
--- a/qgis-app/styles/templates/styles/style_review.html
+++ b/qgis-app/styles/templates/styles/style_review.html
@@ -87,4 +87,4 @@ {{ style_detail.name|title }} in review
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/qgis-app/styles/templates/styles/style_update_form.html b/qgis-app/styles/templates/styles/style_update_form.html
index 9999d723..aabb47b7 100644
--- a/qgis-app/styles/templates/styles/style_update_form.html
+++ b/qgis-app/styles/templates/styles/style_update_form.html
@@ -18,4 +18,4 @@ {% trans "Update Style:" %} {{ style.name }}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/qgis-app/styles/templatetags/styles_custom_tags.py b/qgis-app/styles/templatetags/styles_custom_tags.py
index 0a58e854..12b378de 100644
--- a/qgis-app/styles/templatetags/styles_custom_tags.py
+++ b/qgis-app/styles/templatetags/styles_custom_tags.py
@@ -23,22 +23,22 @@ def anchor_sort_arrow(name, order_by, current_order, current_query):
class_arrow = ""
if not current_query:
- current_query = ''
+ current_query = ""
if not desc_current_order:
result = (
'%s '
''
''
- '' % (
- name, order_by, current_query, class_arrow,
- order_by, current_query))
+ ''
+ % (name, order_by, current_query, class_arrow, order_by, current_query)
+ )
else:
result = (
'%s '
''
''
- '' % (
- name, order_by, current_query,
- order_by, current_query, class_arrow))
+ ''
+ % (name, order_by, current_query, order_by, current_query, class_arrow)
+ )
return mark_safe(result)
diff --git a/qgis-app/styles/tests/test_filehandler.py b/qgis-app/styles/tests/test_filehandler.py
index 935fbfb8..88191070 100644
--- a/qgis-app/styles/tests/test_filehandler.py
+++ b/qgis-app/styles/tests/test_filehandler.py
@@ -3,11 +3,9 @@
from django.core.exceptions import ValidationError
from django.test import TestCase
-
from styles.file_handler import read_xml_style, validator
-STYLEFILE_DIR = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'stylefiles'))
+STYLEFILE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "stylefiles"))
class XMLFileReadTest(TestCase):
@@ -24,80 +22,73 @@ class XMLFileReadTest(TestCase):
- Legend Patch.
- 3D Symbol.
"""
+
def setUp(self):
"""
Setup before each test.
"""
- self.label_setting_file = os.path.join(
- STYLEFILE_DIR, "labelsettings.xml")
- self.legend_patch_file = os.path.join(
- STYLEFILE_DIR, "legend_patch.xml")
- self.color_ramp_file = os.path.join(
- STYLEFILE_DIR, "colorramp_blue.xml")
- self.symbol3d_file = os.path.join(
- STYLEFILE_DIR, "3d_cube.xml")
- self.text_format_file = os.path.join(
- STYLEFILE_DIR, "text-format.xml")
- self.symbol_line_file = os.path.join(
- STYLEFILE_DIR, "cattrail.xml")
- self.symbol_fill_file = os.path.join(
- STYLEFILE_DIR, "topo_swamp.xml")
- self.symbol_marker_file = os.path.join(
- STYLEFILE_DIR, "dotblack_marker.xml")
+ self.label_setting_file = os.path.join(STYLEFILE_DIR, "labelsettings.xml")
+ self.legend_patch_file = os.path.join(STYLEFILE_DIR, "legend_patch.xml")
+ self.color_ramp_file = os.path.join(STYLEFILE_DIR, "colorramp_blue.xml")
+ self.symbol3d_file = os.path.join(STYLEFILE_DIR, "3d_cube.xml")
+ self.text_format_file = os.path.join(STYLEFILE_DIR, "text-format.xml")
+ self.symbol_line_file = os.path.join(STYLEFILE_DIR, "cattrail.xml")
+ self.symbol_fill_file = os.path.join(STYLEFILE_DIR, "topo_swamp.xml")
+ self.symbol_marker_file = os.path.join(STYLEFILE_DIR, "dotblack_marker.xml")
def test_read_label_setting_file(self):
read_file = read_xml_style(self.label_setting_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "labelsetting")
self.assertEqual(style_name, "Basic Label Setting")
def test_read_legend_patch_file(self):
read_file = read_xml_style(self.legend_patch_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "legendpatchshape")
self.assertEqual(style_name, "Building 3")
def test_read_color_ramp_file(self):
read_file = read_xml_style(self.color_ramp_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "colorramp")
self.assertEqual(style_name, "Blues")
def test_read_symbol3d_file(self):
read_file = read_xml_style(self.symbol3d_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "symbol3d")
self.assertEqual(style_name, "Cube")
def test_read_text_format_file(self):
read_file = read_xml_style(self.text_format_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "textformat")
self.assertEqual(style_name, "Basic Label")
def test_read_symbol_line_file(self):
read_file = read_xml_style(self.symbol_line_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "line")
self.assertEqual(style_name, "cat trail")
def test_read_symbol_fill_file(self):
read_file = read_xml_style(self.symbol_fill_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "fill")
self.assertEqual(style_name, "topo swamp")
def test_read_symbol_marker_file(self):
read_file = read_xml_style(self.symbol_marker_file)
- style_type = read_file['type']
- style_name = read_file['name']
+ style_type = read_file["type"]
+ style_name = read_file["name"]
self.assertEqual(style_type, "marker")
self.assertEqual(style_name, "dot black")
@@ -111,8 +102,9 @@ class TestXMLValidator(TestCase):
"""
def test_valid_file(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".xml")
- tf.write("""
+ tf = NamedTemporaryFile(mode="w+t", suffix=".xml")
+ tf.write(
+ """
@@ -131,15 +123,17 @@ def test_valid_file(self):
- """)
+ """
+ )
tf.seek(0)
validation = validator(tf)
self.assertEqual(validation, True)
tf.close()
def test_invalid_xml(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".xml")
- tf.write("""
+ tf = NamedTemporaryFile(mode="w+t", suffix=".xml")
+ tf.write(
+ """
@@ -157,18 +151,21 @@ def test_invalid_xml(self):
- """)
+ """
+ )
tf.seek(0)
self.assertRaises(ValidationError, validator, tf)
- with self.assertRaisesMessage(ValidationError,
- "Cannot parse the style file. Please ensure your file is correct."
+ with self.assertRaisesMessage(
+ ValidationError,
+ "Cannot parse the style file. Please ensure your file is correct.",
):
validator(tf)
tf.close()
def test_invalid_tag_root(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".xml")
- tf.write("""
+ tf = NamedTemporaryFile(mode="w+t", suffix=".xml")
+ tf.write(
+ """
@@ -188,17 +185,21 @@ def test_invalid_tag_root(self):
- """)
+ """
+ )
tf.seek(0)
# self.assertRaises(ValidationError, validator, tf)
- with self.assertRaisesMessage(ValidationError, "Invalid root tag of "
- "style file. Please ensure your file is correct."):
+ with self.assertRaisesMessage(
+ ValidationError,
+ "Invalid root tag of " "style file. Please ensure your file is correct.",
+ ):
validator(tf)
tf.close()
def test_undefined_name_style(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".xml")
- tf.write("""
+ tf = NamedTemporaryFile(mode="w+t", suffix=".xml")
+ tf.write(
+ """
@@ -218,16 +219,19 @@ def test_undefined_name_style(self):
- """)
+ """
+ )
tf.seek(0)
- with self.assertRaisesMessage(ValidationError,
- "Undefined style name. Please register your style type."):
+ with self.assertRaisesMessage(
+ ValidationError, "Undefined style name. Please register your style type."
+ ):
validator(tf)
tf.close()
def test_undefined_type_style(self):
- tf = NamedTemporaryFile(mode='w+t', suffix=".xml")
- tf.write("""
+ tf = NamedTemporaryFile(mode="w+t", suffix=".xml")
+ tf.write(
+ """
- """)
+ """
+ )
tf.seek(0)
- with self.assertRaisesMessage(ValidationError,
- "Undefined style type. Please register your style type."):
+ with self.assertRaisesMessage(
+ ValidationError, "Undefined style type. Please register your style type."
+ ):
validator(tf)
tf.close()
diff --git a/qgis-app/styles/tests/test_models.py b/qgis-app/styles/tests/test_models.py
index dd6c0d30..8e09eb68 100644
--- a/qgis-app/styles/tests/test_models.py
+++ b/qgis-app/styles/tests/test_models.py
@@ -1,6 +1,7 @@
import tempfile
-from django.test import TestCase
+
from django.contrib.auth.models import User
+from django.test import TestCase
from styles.models import Style, StyleType
@@ -16,43 +17,55 @@ def setUp(self):
# https://stackoverflow.com/a/32814129/10268058
self.image_temp = tempfile.NamedTemporaryFile(suffix=".png").name
self.xml_temp = tempfile.NamedTemporaryFile(suffix=".xml").name
- self.marker_type = StyleType.objects.create(symbol_type="marker",
- name="Marker",
- description="a marker for testing purpose",
- icon=self.image_temp)
- self.line_type = StyleType.objects.create(symbol_type="line",
- name="Line",
- description="a line for testing purpose",
- icon=self.image_temp)
- self.user_staff = User.objects.create(username='usertest_staff',
- first_name="first_name_staff",
- last_name="last_name_staff",
- email="staff@example.com",
- password="passwordtest",
- is_active=True,
- is_staff=True,
- is_superuser=False)
- self.style_zero = Style.objects.create(name="style_zero",
+ self.marker_type = StyleType.objects.create(
+ symbol_type="marker",
+ name="Marker",
+ description="a marker for testing purpose",
+ icon=self.image_temp,
+ )
+ self.line_type = StyleType.objects.create(
+ symbol_type="line",
+ name="Line",
+ description="a line for testing purpose",
+ icon=self.image_temp,
+ )
+ self.user_staff = User.objects.create(
+ username="usertest_staff",
+ first_name="first_name_staff",
+ last_name="last_name_staff",
+ email="staff@example.com",
+ password="passwordtest",
+ is_active=True,
+ is_staff=True,
+ is_superuser=False,
+ )
+ self.style_zero = Style.objects.create(
+ name="style_zero",
description="a style for testing purpose",
creator=self.user_staff,
thumbnail_image=self.image_temp,
file=self.xml_temp,
- style_type=self.marker_type)
+ style_type=self.marker_type,
+ )
def test_create_style_type(self):
- fill_type = StyleType.objects.create(symbol_type="fill",
- name="Fill",
- description="a fill for testing purpose",
- icon=self.image_temp)
+ fill_type = StyleType.objects.create(
+ symbol_type="fill",
+ name="Fill",
+ description="a fill for testing purpose",
+ icon=self.image_temp,
+ )
self.assertEqual(fill_type.__str__(), "Fill")
def test_create_style(self):
- style_one = Style.objects.create(name="style_one",
- description="a style for testing purpose",
- creator=self.user_staff,
- thumbnail_image=self.image_temp,
- file=self.xml_temp,
- style_type=self.line_type)
+ style_one = Style.objects.create(
+ name="style_one",
+ description="a style for testing purpose",
+ creator=self.user_staff,
+ thumbnail_image=self.image_temp,
+ file=self.xml_temp,
+ style_type=self.line_type,
+ )
self.assertEqual(style_one.name, "style_one")
self.assertEqual(style_one.creator.first_name, "first_name_staff")
self.assertEqual(style_one.style_type.name, "Line")
diff --git a/qgis-app/styles/tests/test_views.py b/qgis-app/styles/tests/test_views.py
index 9e5408ed..545acda7 100644
--- a/qgis-app/styles/tests/test_views.py
+++ b/qgis-app/styles/tests/test_views.py
@@ -1,24 +1,22 @@
import os
import tempfile
+from base.views.processing_view import resource_notify, resource_update_notify
from django.conf import settings
+from django.contrib.auth.models import Group, User
from django.core import mail
-from django.test import TestCase, Client, override_settings
+from django.test import Client, TestCase, override_settings
from django.urls import reverse
-from django.contrib.auth.models import User, Group
-
from styles.models import Style, StyleType
-from base.views. processing_view import (resource_update_notify,
- resource_notify)
STYLE_DIR = os.path.join(os.path.dirname(__file__), "stylefiles")
class TestPageUserAnonymous(TestCase):
- fixtures = ['fixtures/simplemenu.json']
+ fixtures = ["fixtures/simplemenu.json"]
def test_url(self):
- url = reverse('style_list')
+ url = reverse("style_list")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "All Styles")
@@ -26,13 +24,13 @@ def test_url(self):
self.assertContains(response, "No data.")
def test_upload(self):
- url = reverse('style_create')
+ url = reverse("style_create")
response = self.client.get(url)
- self.assertRedirects(response, '/accounts/login/?next=/styles/add/')
+ self.assertRedirects(response, "/accounts/login/?next=/styles/add/")
class TestUploadStyle(TestCase):
- fixtures = ['fixtures/auth.json', 'fixtures/simplemenu.json']
+ fixtures = ["fixtures/auth.json", "fixtures/simplemenu.json"]
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
def setUp(self):
@@ -49,42 +47,44 @@ def setUp(self):
self.group.user_set.add(self.staff)
# user is logging in to upload page
self.client.login(username="creator", password="password")
- url = reverse('style_create')
+ url = reverse("style_create")
self.response = self.client.get(url)
def test_upload_page_with_login(self):
self.assertEqual(self.response.status_code, 200)
- self.assertTemplateUsed(self.response, 'base/upload_form.html')
- self.assertContains(self.response, 'Style')
+ self.assertTemplateUsed(self.response, "base/upload_form.html")
+ self.assertContains(self.response, "Style")
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
def test_upload_xml_file(self):
- url = reverse('style_create')
+ url = reverse("style_create")
f = os.path.join(STYLE_DIR, "cattrail.xml")
with open(f) as xml_file:
- self.client.post(url, {
- 'file': xml_file,
- 'thumbnail_image': self.thumbnail,
- 'description': 'This style is for testing only purpose'
- })
+ self.client.post(
+ url,
+ {
+ "file": xml_file,
+ "thumbnail_image": self.thumbnail,
+ "description": "This style is for testing only purpose",
+ },
+ )
self.assertEqual(self.response.status_code, 200)
# style should be in Waiting Review
- url = reverse('style_unapproved')
+ url = reverse("style_unapproved")
self.response = self.client.get(url)
self.assertContains(self.response, "1 record found.")
self.assertContains(self.response, "Cat Trail")
self.assertContains(self.response, "Line")
self.assertContains(self.response, "Creator")
# style should not be in Requiring Update
- url = reverse('style_require_action')
+ url = reverse("style_require_action")
self.response = self.client.get(url)
self.assertContains(self.response, "No data.")
-@override_settings(
- EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend')
+@override_settings(EMAIL_BACKEND="django.core.mail.backends.console.EmailBackend")
class TestModeration(TestCase):
- fixtures = ['fixtures/auth.json', 'fixtures/simplemenu.json']
+ fixtures = ["fixtures/auth.json", "fixtures/simplemenu.json"]
@classmethod
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
@@ -106,14 +106,17 @@ def setUpTestData(cls):
# upload a style xml
c = Client()
c.login(username="creator", password="password")
- url = reverse('style_create')
+ url = reverse("style_create")
f = os.path.join(STYLE_DIR, "legend_patch.xml")
with open(f) as xml_file:
- c.post(url, {
- 'file': xml_file,
- 'thumbnail_image': cls.thumbnail,
- 'description': 'This style is for testing only purpose'
- })
+ c.post(
+ url,
+ {
+ "file": xml_file,
+ "thumbnail_image": cls.thumbnail,
+ "description": "This style is for testing only purpose",
+ },
+ )
c.logout()
cls.uploaded_style = Style.objects.filter(name="Building 3").first()
@@ -126,7 +129,7 @@ def tearDown(self):
settings.MEDIA_ROOT = self._original_media_root
def test_user_anonymous_should_not_see_moderation_link(self):
- url = reverse('style_list')
+ url = reverse("style_list")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "All Styles")
@@ -134,58 +137,54 @@ def test_user_anonymous_should_not_see_moderation_link(self):
self.assertNotContains(response, "Requiring Update")
def test_user_anonymous_should_redirect_from_moderation(self):
- url = reverse('style_unapproved')
+ url = reverse("style_unapproved")
response = self.client.get(url)
- self.assertRedirects(
- response,
- '/accounts/login/?next=/styles/unapproved/')
- url = reverse('style_require_action')
+ self.assertRedirects(response, "/accounts/login/?next=/styles/unapproved/")
+ url = reverse("style_require_action")
response = self.client.get(url)
- self.assertRedirects(
- response,
- '/accounts/login/?next=/styles/require_action/')
+ self.assertRedirects(response, "/accounts/login/?next=/styles/require_action/")
def test_creator_should_see_moderation_list(self):
self.client.login(username="creator", password="password")
- url = reverse('style_list')
+ url = reverse("style_list")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "All Styles")
self.assertContains(response, "Waiting Review")
# go to waiting review page
- url = reverse('style_unapproved')
+ url = reverse("style_unapproved")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "1 record found.")
self.assertContains(response, "Building 3")
self.assertContains(response, "Legendpatchshape")
# do detail review
- url = reverse('style_detail', kwargs={'pk': self.uploaded_style.id})
+ url = reverse("style_detail", kwargs={"pk": self.uploaded_style.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Building 3")
self.assertContains(response, "in review")
self.assertNotContains(response, '
{% endblock %}
-
diff --git a/qgis-app/templates/feedjack/post.html b/qgis-app/templates/feedjack/post.html
index 90b06d3b..68e85f58 100755
--- a/qgis-app/templates/feedjack/post.html
+++ b/qgis-app/templates/feedjack/post.html
@@ -24,4 +24,3 @@