Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QLR sharing file feature #219

Merged
merged 14 commits into from
Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ jobs:
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
59 changes: 0 additions & 59 deletions qgis-app/base/templatetags/resources_custom_tags.py

This file was deleted.

13 changes: 13 additions & 0 deletions qgis-app/base/views/processing_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ class ResourceBaseCreateView(LoginRequiredMixin,

template_name = 'base/upload_form.html'
is_1mb_limit_enable = True
is_custom_license_agreement = False

def form_valid(self, form):
self.obj = form.save(commit=False)
Expand All @@ -247,6 +248,8 @@ def get_success_url(self):
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
return context


Expand All @@ -266,6 +269,7 @@ class ResourceBaseDetailView(ResourceBaseContextMixin,
css = ()

is_3d_model = False
license_template = 'base/includes/license.html'

def get_template_names(self):
object = self.get_object()
Expand All @@ -283,6 +287,7 @@ def get_context_data(self, **kwargs):
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" % (
Expand All @@ -307,6 +312,7 @@ class ResourceBaseUpdateView(LoginRequiredMixin,

context_object_name = 'object'
template_name = 'base/update_form.html'
is_custom_license_agreement = False

def dispatch(self, request, *args, **kwargs):
object = self.get_object()
Expand All @@ -328,6 +334,13 @@ def form_valid(self, form):
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
return context


@method_decorator(never_cache, name='dispatch')
class ResourceBaseListView(ResourceBaseContextMixin,
Expand Down
19 changes: 19 additions & 0 deletions qgis-app/layerdefinitions/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib import admin
from layerdefinitions.models import LayerDefinition, Review


class LayerDefinitionInline(admin.TabularInline):
model = Review
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')


@admin.register(Review)
class LayerDefinitionReviewAdmin(admin.ModelAdmin):
list_display = ('resource', 'reviewer', 'comment', 'review_date',)
5 changes: 5 additions & 0 deletions qgis-app/layerdefinitions/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class LayerdefinitionsConfig(AppConfig):
name = 'layerdefinitions'
53 changes: 53 additions & 0 deletions qgis-app/layerdefinitions/file_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""
Validator for QLR file.
"""

import re
import xml.etree.ElementTree as ET

from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _


def parse_qlr(xmlfile):
xmlfile.seek(0)
try:
tree = ET.parse(xmlfile)
except ET.ParseError:
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.'))
return True


def get_url_datasource(xmlfile):
tree = parse_qlr(xmlfile)
root = tree.getroot()
datasource = root.find('./maplayers/maplayer/datasource')
rgx = r'(?<=url=)[\'"]?(.*?)[\'"&\s]*?$'
try:
url = re.findall(rgx, datasource.text)
except TypeError:
return None
except AttributeError:
return None
result = url[0] if url else None
return result


def get_provider(xmlfile):
tree = parse_qlr(xmlfile)
root = tree.getroot()
provider = root.find('./maplayers/maplayer/provider')
try:
return provider.text
except AttributeError:
return None
27 changes: 27 additions & 0 deletions qgis-app/layerdefinitions/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django import forms

from layerdefinitions.models import LayerDefinition
from layerdefinitions.file_handler import validator

from base.forms.processing_forms import ResourceBaseCleanFileForm


class ResourceFormMixin(forms.ModelForm):
class Meta:
model = LayerDefinition
fields = [
'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)
return file


class UpdateForm(ResourceFormMixin):
"""Update Form."""
33 changes: 33 additions & 0 deletions qgis-app/layerdefinitions/license.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import io
import os
import shutil
import uuid
import zipfile


def create_license_file(string):
tmp_dir = f'/tmp/{uuid.uuid4().hex}'
file = f'{tmp_dir}/license.txt'
os.mkdir(tmp_dir)
with open(file, 'w') as f:
f.write(string)
f.seek(0)
return file, tmp_dir


def zipped_with_license(file, zip_subdir, custom_license):
license_file, tmp_dir = create_license_file(custom_license)
filenames = (file, license_file)
in_memory_data = io.BytesIO()
zf = zipfile.ZipFile(in_memory_data, "w")

for fpath in filenames:
fdir, fname = os.path.split(fpath)
zip_path = os.path.join(zip_subdir, fname)
zf.write(fpath, zip_path)

zf.close()

shutil.rmtree(tmp_dir)

return in_memory_data
57 changes: 57 additions & 0 deletions qgis-app/layerdefinitions/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Generated by Django 2.2.24 on 2021-11-29 18:32

from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
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')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
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')),
],
options={
'ordering': ['review_date'],
'abstract': False,
},
),
]
Empty file.
Loading