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

Migrate to QGIS 3 + Python 3 #59

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
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
5 changes: 1 addition & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ sudo: required
language: python

python:
- "2.7"
- "3.6"

env:
global:
@@ -23,9 +23,6 @@ env:
HEADLESS_COMMAND=dev
SUBSEQUENT_RUN_TESTING=True

virtualenv:
system_site_packages: true

services:
- docker

2 changes: 1 addition & 1 deletion REQUIREMENTS-TRAVIS.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ansible==2.3.1.0
ansible>=2.6.18
codecov
flake8==3.6.0
2 changes: 1 addition & 1 deletion deployment/ansible/development/group_vars/all.travis.yml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ interpreters:
inasafe:
repo: https://github.com/inasafe/inasafe.git
remote: upstream
version: inasafe_4
version: maintenance-fix
depth: 1

inasafe_headless_worker:
13 changes: 8 additions & 5 deletions deployment/docker-headless/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------
FROM kartoza/qgis-desktop:2.18
FROM qgis/qgis:release-3_14

RUN apt-get -y update; apt-get -y --force-yes install pwgen git inotify-tools

@@ -9,7 +9,10 @@ RUN apt-get -y update; apt-get -y --force-yes install pwgen git inotify-tools
RUN apt-get update -y; apt-get install -y --force-yes openssh-server sudo
RUN mkdir /var/run/sshd
RUN echo 'root:docker' | chpasswd
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
# Comment out PermitRootLogin setting, whatever it is
RUN sed -i 's/^PermitRootLogin */#PermitRootLogin /' /etc/ssh/sshd_config
# Write out PermitRootLogin setting at the end
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
@@ -21,8 +24,7 @@ RUN echo "export VISIBLE=now" >> /etc/profile

#-------------Application Specific Stuff ----------------------------------------------------
# Install git, xvfb
RUN apt-get -y update; apt-get -y --force-yes install git xvfb python-setuptools python-dev libssl-dev libffi-dev python-scipy
RUN easy_install pip==9.0.1
RUN apt-get -y update; apt-get -y --force-yes install git xvfb python3-setuptools python3-dev libssl-dev libffi-dev python3-scipy
# Copy ubuntu fonts
RUN apt-get -y update; apt-get -y --force-yes install wget unzip
ADD ubuntu-font-family-0.83.zip /ubuntu-font-family-0.83.zip
@@ -31,8 +33,9 @@ RUN mv ubuntu-font-family-0.83 /usr/share/fonts/truetype/ubuntu-font-family
RUN fc-cache -f -v

# This image instance uses dist-packages directory
ADD sitecustomize.py /usr/local/lib/python2.7/dist-packages/sitecustomize.py
ADD sitecustomize.py /usr/lib/python3/dist-packages/sitecustomize.py
ADD REQUIREMENTS.txt /REQUIREMENTS.txt
RUN ln -s /usr/bin/pip3 /usr/bin/pip
RUN pip install -r REQUIREMENTS.txt

ADD docker-entrypoint.sh /docker-entrypoint.sh
2 changes: 1 addition & 1 deletion deployment/docker-headless/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

# Wait run xvfb
while [ -z "$(pidof /usr/bin/Xvfb)" ]; do
start-stop-daemon --start -b -x /usr/bin/Xvfb ${DISPLAY}
start-stop-daemon --start -b -x /usr/bin/Xvfb ${DISPLAY} -- -screen 0 1024x768x24 -ac +extension GLX +render -noreset -nolisten tcp
sleep 5
done

8 changes: 4 additions & 4 deletions deployment/production/docker/headless/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#--------- Generic stuff all our Dockerfiles should start with so we get caching ------------
FROM kartoza/qgis-desktop:2.18
FROM qgis/qgis:release-3_14

RUN apt-get -y update; apt-get -y --force-yes install pwgen git inotify-tools

#-------------Application Specific Stuff ----------------------------------------------------
# Install git, xvfb
RUN apt-get -y update; apt-get -y --force-yes install git xvfb python-setuptools python-dev libssl-dev libffi-dev python-scipy
RUN easy_install pip==9.0.1
RUN apt-get -y update; apt-get -y --force-yes install git xvfb python3-setuptools python3-dev libssl-dev libffi-dev python3-scipy
# Copy ubuntu fonts
RUN apt-get -y update; apt-get -y --force-yes install wget unzip
ADD ubuntu-font-family-0.83.zip /ubuntu-font-family-0.83.zip
@@ -15,6 +14,7 @@ RUN mv ubuntu-font-family-0.83 /usr/share/fonts/truetype/ubuntu-font-family
RUN fc-cache -f -v

ADD REQUIREMENTS.txt /REQUIREMENTS.txt
RUN ln -s /usr/bin/pip3 /usr/bin/pip
RUN pip install -r REQUIREMENTS.txt

ADD docker-entrypoint.sh /docker-entrypoint.sh
@@ -23,7 +23,7 @@ RUN chmod +x /docker-entrypoint.sh
# Install InaSAFE Core
RUN mkdir -p /usr/share/qgis/python/plugins
WORKDIR /usr/share/qgis/python/plugins
ARG INASAFE_CORE_TAG=version-4_4_0
ARG INASAFE_CORE_TAG=develop
RUN git clone --branch ${INASAFE_CORE_TAG} --depth 1 --recursive https://github.com/inasafe/inasafe.git inasafe

# Install InaSAFE Headless
3 changes: 2 additions & 1 deletion deployment/production/docker/headless/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -2,11 +2,12 @@

# Wait run xvfb
while [ -z "$(pidof /usr/bin/Xvfb)" ]; do
start-stop-daemon --start -b -x /usr/bin/Xvfb ${DISPLAY}
start-stop-daemon --start -b -x /usr/bin/Xvfb ${DISPLAY} -- -screen 0 1024x768x24 -ac +extension GLX +render -noreset -nolisten tcp
sleep 5
done

cp -n /home/app/headless/celeryconfig_sample.py /home/app/headless/celeryconfig.py
echo "Config file copied"

if [ $# -eq 2 ] && [ $1 = "prod" ] && [ $2 = "inasafe-headless-worker" ]; then
/usr/local/bin/celery -A headless.celery_app worker -l info -Q inasafe-headless -n inasafe-headless.%h
7 changes: 5 additions & 2 deletions src/headless/REQUIREMENTS.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
requests>=2.6.2
tzlocal==1.2
celery==4.1.0
nosexcover
hammock==0.2.4
nose2==0.6.5
coverage==4.4.2
threadpool==1.3.2
pyinotify==0.9.6
celery==4.1.1
raven==5.29.0
mock==2.0.0
nose==1.3.7
3 changes: 2 additions & 1 deletion src/headless/celery_app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding=utf-8
import importlib
from importlib import reload
import json
import os

@@ -30,7 +31,7 @@ def load_inasafe_settings():
from safe.definitions import default_settings
from safe.utilities.settings import set_setting

for key, value in default_settings.inasafe_default_settings.iteritems():
for key, value in list(default_settings.inasafe_default_settings.items()):
set_setting(key, value)

# Override settings from INASAFE_SETTINGS_PATH
1 change: 1 addition & 0 deletions src/headless/settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding=utf-8
"""InaSAFE Headless settings."""
from builtins import str
import logging
from distutils.util import strtobool

22 changes: 11 additions & 11 deletions src/headless/tasks/inasafe_analysis.py
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@

from copy import deepcopy
from datetime import datetime
from PyQt4.QtCore import QUrl
from qgis.PyQt.QtCore import QUrl

from qgis.core import (
QgsCoordinateReferenceSystem, QgsMapLayerRegistry, QgsProject)
QgsCoordinateReferenceSystem, QgsProject)

from safe.definitions.constants import (
PREPARE_SUCCESS, ANALYSIS_SUCCESS, MULTI_EXPOSURE_ANALYSIS_FLAG)
@@ -55,7 +55,7 @@ def clean_metadata(metadata):
:param metadata: Metadata as dictionary.
:type metadata: dict
"""
for key, value in metadata.items():
for key, value in list(metadata.items()):
if isinstance(value, dict):
clean_metadata(value)
if isinstance(value, QUrl):
@@ -118,7 +118,7 @@ def inasafe_analysis(
"""
# Clean up layer registry before using
# In case previous task exited prematurely before cleanup
layer_registry = QgsMapLayerRegistry.instance()
layer_registry = QgsProject.instance()
layer_registry.removeAllMapLayers()

impact_function = ImpactFunction()
@@ -213,7 +213,7 @@ def inasafe_multi_exposure_analysis(
"""
# Clean up layer registry before using
# In case previous task exited prematurely before cleanup
layer_registry = QgsMapLayerRegistry.instance()
layer_registry = QgsProject.instance()
layer_registry.removeAllMapLayers()

multi_exposure_if = MultiExposureImpactFunction()
@@ -324,7 +324,7 @@ def generate_report(
"""
# Clean up layer registry before using
# In case previous task exited prematurely before cleanup
layer_registry = QgsMapLayerRegistry.instance()
layer_registry = QgsProject.instance()
layer_registry.removeAllMapLayers()

output_metadata = read_iso19115_metadata(impact_layer_uri)
@@ -344,14 +344,14 @@ def generate_report(
root = QgsProject.instance().layerTreeRoot()

group_analysis = root.insertGroup(0, impact_function.name)
group_analysis.setVisible(True)
group_analysis.setItemVisibilityChecked(True)
group_analysis.setCustomProperty(
MULTI_EXPOSURE_ANALYSIS_FLAG, True)

for layer in impact_function.outputs:
QgsMapLayerRegistry.instance().addMapLayer(layer, False)
QgsProject.instance().addMapLayer(layer, False)
layer_node = group_analysis.addLayer(layer)
layer_node.setVisible(False)
layer_node.setItemVisibilityChecked(False)

# set layer title if any
try:
@@ -362,7 +362,7 @@ def generate_report(

for analysis in impact_function.impact_functions:
detailed_group = group_analysis.insertGroup(0, analysis.name)
detailed_group.setVisible(True)
detailed_group.setItemVisibilityChecked(True)
add_impact_layers_to_canvas(analysis, group=detailed_group)
else:
impact_function = (
@@ -515,7 +515,7 @@ def push_to_geonode(layer_uri):
'username': REALTIME_GEONODE_USER,
'password': REALTIME_GEONODE_PASSWORD
}
for key, value in requirements.items():
for key, value in list(requirements.items()):
if not value:
message = 'Can not upload to geonode because the %s is empty' % key
LOGGER.warning(message)
2 changes: 1 addition & 1 deletion src/headless/tasks/inasafe_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# coding=utf-8
"""Task for InaSAFE Headless."""

from importlib import reload
from headless.celery_app import app, start_inasafe
from headless.tasks import inasafe_analysis
from headless.utils import get_headless_logger
71 changes: 31 additions & 40 deletions src/headless/tasks/test/data/input_layers/buildings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" ?>
<gmd:MD_Metadata xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.isotc211.org/2005/gmd http://www.isotc211.org/2005/gmd/gmd.xsd">
<gmd:fileIdentifier>
<gco:CharacterString>None</gco:CharacterString>
@@ -171,57 +171,48 @@
</gmd:extent>
<gmd:supplementalInformation>
<inasafe>
<classification>
<gco:CharacterString>generic_structure_classes</gco:CharacterString>
</classification>
<inasafe_fields>
<gco:Dictionary>{&quot;exposure_id_field&quot;: &quot;exposure_id&quot;, &quot;exposure_type_field&quot;: &quot;exposure_type&quot;}</gco:Dictionary>
</inasafe_fields>
<inasafe_default_values>
<gco:Dictionary/>
</inasafe_default_values>
<layer_purpose>
<gco:CharacterString>exposure</gco:CharacterString>
</layer_purpose>
<layer_mode>
<gco:CharacterString>classified</gco:CharacterString>
</layer_mode>
<layer_geometry>
<gco:CharacterString>polygon</gco:CharacterString>
</layer_geometry>
<keyword_version>
<gco:CharacterString>5.0</gco:CharacterString>
</keyword_version>
<scale>
<gco:CharacterString/>
</scale>
<multipart_polygon>
<gco:Boolean/>
</multipart_polygon>
<source>
<gco:CharacterString>Test layer</gco:CharacterString>
</source>
<value_map>
<gco:Dictionary>{&quot;commercial&quot;: [&quot;shop&quot;], &quot;education&quot;: [&quot;school&quot;], &quot;health&quot;: [&quot;hospital&quot;], &quot;government&quot;: [&quot;ministry&quot;]}</gco:Dictionary>
</value_map>
<layer_geometry>
<gco:CharacterString>polygon</gco:CharacterString>
</layer_geometry>
<inasafe_fields>
<gco:Dictionary>{&quot;exposure_id_field&quot;: &quot;exposure_id&quot;, &quot;exposure_type_field&quot;: &quot;exposure_type&quot;}</gco:Dictionary>
</inasafe_fields>
<inasafe_default_values>
<gco:Dictionary/>
</inasafe_default_values>
<extra_keywords>
<gco:Dictionary/>
</extra_keywords>
<exposure>
<gco:CharacterString>structure</gco:CharacterString>
</exposure>
<report>
<gco:CharacterString/>
</report>
<exposure_unit>
<gco:CharacterString/>
</exposure_unit>
<keyword_version>
<gco:CharacterString>4.0</gco:CharacterString>
</keyword_version>
<datatype>
<gco:CharacterString/>
</datatype>
<allow_resampling>
<gco:CharacterString/>
</allow_resampling>
<layer_purpose>
<gco:CharacterString>exposure</gco:CharacterString>
</layer_purpose>
<resolution>
<gco:FloatTuple/>
</resolution>
<layer_mode>
<gco:CharacterString>classified</gco:CharacterString>
</layer_mode>
<classification>
<gco:CharacterString>generic_structure_classes</gco:CharacterString>
</classification>
<value_map>
<gco:Dictionary>{&quot;commercial&quot;: [&quot;shop&quot;], &quot;education&quot;: [&quot;school&quot;], &quot;health&quot;: [&quot;hospital&quot;], &quot;government&quot;: [&quot;ministry&quot;]}</gco:Dictionary>
</value_map>
<active_band>
<gco:Integer/>
</active_band>
</inasafe>
</gmd:supplementalInformation>
</gmd:MD_DataIdentification>
216 changes: 216 additions & 0 deletions src/headless/tasks/test/data/input_layers/buildings_geojson.qlr
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
<!DOCTYPE qgis-layer-definition>
<qlr>
<layer-tree-group checked="Qt::Checked" expanded="1" name="">
<customproperties/>
<layer-tree-layer source="./buildings.geojson" providerKey="ogr" checked="Qt::Checked" patch_size="-1,-1" expanded="1" name="buildings" legend_exp="" id="buildings_25dafc4f_de52_4c06_94ab_14cd6a301be1" legend_split_behavior="0">
<customproperties/>
</layer-tree-layer>
</layer-tree-group>
<maplayers>
<maplayer wkbType="Polygon" geometry="Polygon" type="vector" simplifyDrawingTol="1" refreshOnNotifyMessage="" refreshOnNotifyEnabled="0" simplifyLocal="1" autoRefreshTime="0" hasScaleBasedVisibilityFlag="0" simplifyDrawingHints="1" maxScale="0" simplifyAlgorithm="0" labelsEnabled="0" simplifyMaxScale="1" autoRefreshEnabled="0" readOnly="0" styleCategories="AllStyleCategories" minScale="100000000">
<extent>
<xmin>106.6388189694232409</xmin>
<ymin>-6.29597524568388156</ymin>
<xmax>106.90657529121263281</xmax>
<ymax>-6.11387787165906804</ymax>
</extent>
<id>buildings_25dafc4f_de52_4c06_94ab_14cd6a301be1</id>
<datasource>./buildings.geojson</datasource>
<keywordList>
<value></value>
</keywordList>
<layername>buildings</layername>
<srs>
<spatialrefsys>
<wkt>GEOGCRS["WGS 84",DATUM["World Geodetic System 1984",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["unknown"],AREA["World"],BBOX[-90,-180,90,180]],ID["EPSG",4326]]</wkt>
<proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
<srsid>3452</srsid>
<srid>4326</srid>
<authid>EPSG:4326</authid>
<description>WGS 84</description>
<projectionacronym>longlat</projectionacronym>
<ellipsoidacronym>EPSG:7030</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</srs>
<resourceMetadata>
<identifier></identifier>
<parentidentifier></parentidentifier>
<language></language>
<type>dataset</type>
<title></title>
<abstract></abstract>
<links/>
<fees></fees>
<encoding></encoding>
<crs>
<spatialrefsys>
<wkt></wkt>
<proj4></proj4>
<srsid>0</srsid>
<srid>0</srid>
<authid></authid>
<description></description>
<projectionacronym></projectionacronym>
<ellipsoidacronym></ellipsoidacronym>
<geographicflag>false</geographicflag>
</spatialrefsys>
</crs>
<extent/>
</resourceMetadata>
<provider encoding="UTF-8">ogr</provider>
<vectorjoins/>
<layerDependencies/>
<dataDependencies/>
<legend type="default-vector"/>
<expressionfields/>
<map-layer-style-manager current="default">
<map-layer-style name="default"/>
</map-layer-style-manager>
<auxiliaryLayer/>
<flags>
<Identifiable>1</Identifiable>
<Removable>1</Removable>
<Searchable>1</Searchable>
</flags>
<temporal enabled="0" accumulate="0" startField="" fixedDuration="0" durationUnit="min" mode="0" endField="" durationField="" startExpression="" endExpression="">
<fixedRange>
<start></start>
<end></end>
</fixedRange>
</temporal>
<renderer-v2 symbollevels="0" enableorderby="0" type="singleSymbol" forceraster="0">
<symbols>
<symbol clip_to_extent="1" type="fill" name="0" alpha="1" force_rhr="0">
<layer locked="0" class="SimpleFill" enabled="1" pass="0">
<prop v="3x:0,0,0,0,0,0" k="border_width_map_unit_scale"/>
<prop v="133,182,111,255" k="color"/>
<prop v="bevel" k="joinstyle"/>
<prop v="0,0" k="offset"/>
<prop v="3x:0,0,0,0,0,0" k="offset_map_unit_scale"/>
<prop v="MM" k="offset_unit"/>
<prop v="35,35,35,255" k="outline_color"/>
<prop v="solid" k="outline_style"/>
<prop v="0.26" k="outline_width"/>
<prop v="MM" k="outline_width_unit"/>
<prop v="solid" k="style"/>
<data_defined_properties>
<Option type="Map">
<Option value="" type="QString" name="name"/>
<Option name="properties"/>
<Option value="collection" type="QString" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</symbols>
<rotation/>
<sizescale/>
</renderer-v2>
<customproperties/>
<blendMode>0</blendMode>
<featureBlendMode>0</featureBlendMode>
<layerOpacity>1</layerOpacity>
<geometryOptions geometryPrecision="0" removeDuplicateNodes="0">
<activeChecks type="StringList">
<Option value="" type="QString"/>
</activeChecks>
<checkConfiguration/>
</geometryOptions>
<referencedLayers/>
<referencingLayers/>
<fieldConfiguration>
<field name="exposure_id">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="exposure_type">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="fake_field">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="absolute">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="ratio">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
</fieldConfiguration>
<aliases>
<alias field="exposure_id" name="" index="0"/>
<alias field="exposure_type" name="" index="1"/>
<alias field="fake_field" name="" index="2"/>
<alias field="absolute" name="" index="3"/>
<alias field="ratio" name="" index="4"/>
</aliases>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<defaults>
<default field="exposure_id" expression="" applyOnUpdate="0"/>
<default field="exposure_type" expression="" applyOnUpdate="0"/>
<default field="fake_field" expression="" applyOnUpdate="0"/>
<default field="absolute" expression="" applyOnUpdate="0"/>
<default field="ratio" expression="" applyOnUpdate="0"/>
</defaults>
<constraints>
<constraint notnull_strength="0" constraints="0" field="exposure_id" unique_strength="0" exp_strength="0"/>
<constraint notnull_strength="0" constraints="0" field="exposure_type" unique_strength="0" exp_strength="0"/>
<constraint notnull_strength="0" constraints="0" field="fake_field" unique_strength="0" exp_strength="0"/>
<constraint notnull_strength="0" constraints="0" field="absolute" unique_strength="0" exp_strength="0"/>
<constraint notnull_strength="0" constraints="0" field="ratio" unique_strength="0" exp_strength="0"/>
</constraints>
<constraintExpressions>
<constraint desc="" field="exposure_id" exp=""/>
<constraint desc="" field="exposure_type" exp=""/>
<constraint desc="" field="fake_field" exp=""/>
<constraint desc="" field="absolute" exp=""/>
<constraint desc="" field="ratio" exp=""/>
</constraintExpressions>
<expressionfields/>
<attributeactions>
<defaultAction key="Canvas" value="{00000000-0000-0000-0000-000000000000}"/>
</attributeactions>
<attributetableconfig actionWidgetStyle="dropDown" sortExpression="" sortOrder="0">
<columns/>
</attributetableconfig>
<conditionalstyles>
<rowstyles/>
<fieldstyles/>
</conditionalstyles>
<storedexpressions/>
<editform tolerant="1"></editform>
<editforminit/>
<editforminitcodesource>0</editforminitcodesource>
<editforminitfilepath></editforminitfilepath>
<editforminitcode><![CDATA[]]></editforminitcode>
<featformsuppress>0</featformsuppress>
<editorlayout>generatedlayout</editorlayout>
<editable/>
<labelOnTop/>
<dataDefinedFieldProperties/>
<widgets/>
<previewExpression></previewExpression>
<mapTip></mapTip>
</maplayer>
</maplayers>
</qlr>
Original file line number Diff line number Diff line change
@@ -420,7 +420,7 @@ Enter the name of the function in the "Python Init function"
field.
An example follows:
"""
from PyQt4.QtGui import QWidget
from qgis.PyQt.QtGui import QWidget

def my_form_open(dialog, layer, feature):
geom = feature.geometry()
2 changes: 1 addition & 1 deletion src/headless/tasks/test/data/input_layers/small_grid.qml
Original file line number Diff line number Diff line change
@@ -220,7 +220,7 @@ Enter the name of the function in the "Python Init function"
field.
An example follows:
"""
from PyQt4.QtGui import QWidget
from qgis.PyQt.QtGui import QWidget

def my_form_open(dialog, layer, feature):
geom = feature.geometry()
3 changes: 2 additions & 1 deletion src/headless/tasks/test/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# coding=utf-8
from builtins import range
import os
from functools import wraps

@@ -23,7 +24,7 @@
buildings_layer_uri = os.path.join(
dir_path, 'data', 'input_layers', 'buildings.geojson')
buildings_layer_qlr_uri = os.path.join(
dir_path, 'data', 'input_layers', 'buildings.qlr')
dir_path, 'data', 'input_layers', 'buildings_geojson.qlr')

shapefile_layer_uri = standard_data_path('exposure', 'airports.shp')
ascii_layer_uri = standard_data_path('gisv4', 'hazard', 'earthquake.asc')
1 change: 1 addition & 0 deletions src/headless/tasks/test/test_celery_task.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding=utf-8
"""Unit test for celery task."""
from past.builtins import basestring
import os
import pickle
import unittest
50 changes: 24 additions & 26 deletions src/headless/tasks/test/test_generate_report.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# coding=utf-8
from __future__ import print_function
from past.builtins import basestring
import os
import unittest
from distutils.util import strtobool

from headless.settings import OUTPUT_DIRECTORY
from headless.tasks.inasafe_analysis import (
@@ -36,9 +37,6 @@

class TestGenerateReport(unittest.TestCase):

@unittest.skipIf(
strtobool(os.environ.get('ON_TRAVIS', 'False')),
"""Skipped because we don't have remote service QLR anymore.""")
@retry_on_worker_lost_error()
def test_generate_report_qlr(self):
"""Test generating report with QLR files."""
@@ -52,7 +50,7 @@ def test_generate_report_qlr(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'],
result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -71,8 +69,8 @@ def test_generate_report_qlr(self):
result = async_result.get()
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
@@ -88,7 +86,7 @@ def test_generate_report_with_aggregation(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -107,8 +105,8 @@ def test_generate_report_with_aggregation(self):
result = async_result.get()
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
@@ -124,7 +122,7 @@ def test_generate_report_without_aggregation(self):
result = result_delay.get()
self.assertEqual(0, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -143,8 +141,8 @@ def test_generate_report_without_aggregation(self):
result = async_result.get()
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
@@ -164,13 +162,13 @@ def test_generate_multi_exposure_report(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
num_exposure_output = 0
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
if isinstance(layer_uri, basestring):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))
elif isinstance(layer_uri, dict):
num_exposure_output += 1
for the_key, the_layer_uri in layer_uri.items():
for the_key, the_layer_uri in list(layer_uri.items()):
self.assertTrue(os.path.exists(the_layer_uri))
self.assertTrue(the_layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -189,8 +187,8 @@ def test_generate_multi_exposure_report(self):
result = async_result.get()
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
@@ -207,7 +205,7 @@ def test_generate_custom_report(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -222,14 +220,14 @@ def test_generate_custom_report(self):
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
product_keys = []
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
product_keys.append(product_key)
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
if custom_map_template_basename == product_key:
print product_uri
print(product_uri)

# Check if custom map template found.
self.assertIn(custom_map_template_basename, product_keys)
@@ -250,7 +248,7 @@ def test_generate_report_with_basemap(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'],
result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -275,14 +273,14 @@ def test_generate_report_with_basemap(self):
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
product_keys = []
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
product_keys.append(product_key)
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
if custom_map_template_basename == product_key:
print product_uri
print(product_uri)

@retry_on_worker_lost_error()
def test_get_generated_report(self):
@@ -293,7 +291,7 @@ def test_get_generated_report(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

33 changes: 15 additions & 18 deletions src/headless/tasks/test/test_run_analysis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# coding=utf-8
from past.builtins import basestring
import os
import unittest
from distutils.util import strtobool

from headless.settings import OUTPUT_DIRECTORY
from headless.tasks.inasafe_wrapper import (
@@ -39,7 +39,7 @@ def test_run_analysis(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -49,7 +49,7 @@ def test_run_analysis(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -68,13 +68,13 @@ def test_run_multi_exposure_analysis(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
num_exposure_output = 0
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
if isinstance(layer_uri, basestring):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))
elif isinstance(layer_uri, dict):
num_exposure_output += 1
for the_key, the_layer_uri in layer_uri.items():
for the_key, the_layer_uri in list(layer_uri.items()):
self.assertTrue(os.path.exists(the_layer_uri))
self.assertTrue(the_layer_uri.startswith(OUTPUT_DIRECTORY))
# Check the number of per exposure output is the same as the number
@@ -88,22 +88,19 @@ def test_run_multi_exposure_analysis(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
num_exposure_output = 0
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
if isinstance(layer_uri, basestring):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))
elif isinstance(layer_uri, dict):
num_exposure_output += 1
for the_key, the_layer_uri in layer_uri.items():
for the_key, the_layer_uri in list(layer_uri.items()):
self.assertTrue(os.path.exists(the_layer_uri))
self.assertTrue(the_layer_uri.startswith(OUTPUT_DIRECTORY))
# Check the number of per exposure output is the same as the number
# of exposures
self.assertEqual(num_exposure_output, len(exposure_layer_uris))

@unittest.skipIf(
strtobool(os.environ.get('ON_TRAVIS', 'False')),
"""Skipped because we don't have remote service QLR anymore.""")
@retry_on_worker_lost_error()
def test_run_analysis_qlr(self):
"""Test running analysis with QLR files."""
@@ -115,7 +112,7 @@ def test_run_analysis_qlr(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'],
result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -126,7 +123,7 @@ def test_run_analysis_qlr(self):
self.assertEqual(ANALYSIS_SUCCESS, result['status'],
result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -140,7 +137,7 @@ def test_run_multilingual_analysis(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -161,8 +158,8 @@ def test_run_multilingual_analysis(self):
result = async_result.get()
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
@@ -173,7 +170,7 @@ def test_run_multilingual_analysis(self):
result = result_delay.get()
self.assertEqual(ANALYSIS_SUCCESS, result['status'], result['message'])
self.assertLess(0, len(result['output']))
for key, layer_uri in result['output'].items():
for key, layer_uri in list(result['output'].items()):
self.assertTrue(os.path.exists(layer_uri))
self.assertTrue(layer_uri.startswith(OUTPUT_DIRECTORY))

@@ -196,8 +193,8 @@ def test_run_multilingual_analysis(self):
result = async_result.get()
self.assertEqual(
ImpactReport.REPORT_GENERATION_SUCCESS, result['status'])
for key, products in result['output'].items():
for product_key, product_uri in products.items():
for key, products in list(result['output'].items()):
for product_key, product_uri in list(products.items()):
message = 'Product %s is not found in %s' % (
product_key, product_uri)
self.assertTrue(os.path.exists(product_uri), message)
4 changes: 2 additions & 2 deletions src/headless/tasks/test/test_subsequent_run.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
import os
import unittest

from qgis.core import QgsMapLayerRegistry
from qgis.core import QgsProject

from headless.celeryconfig import task_always_eager
from headless.tasks.inasafe_wrapper import (
@@ -32,7 +32,7 @@ class TestSubsequentRun(unittest.TestCase):

def check_layer_registry_empty(self):
# Layer registry should be empty between run
layer_registry = QgsMapLayerRegistry.instance()
layer_registry = QgsProject.instance()
self.assertDictEqual(layer_registry.mapLayers(), {})

@unittest.skipUnless(
9 changes: 5 additions & 4 deletions src/headless/utils.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
import logging
import os

from qgis.core import QgsMapLayer
from qgis.core import QgsLayerDefinition

from headless import settings as headless_settings
from safe.common.exceptions import NoKeywordsFoundError
@@ -50,11 +50,12 @@ def load_layer(full_layer_uri_string, name=None, provider=None):
base, ext = os.path.splitext(full_layer_uri_string)

if ext.lower() == '.qlr':
layer = QgsMapLayer.fromLayerDefinitionFile(full_layer_uri_string)
if not layer:
layers = QgsLayerDefinition.loadLayerDefinitionLayers(
full_layer_uri_string)
if not layers:
return None, None

layer = layer[0]
layer = layers[0]
if layer.isValid():
keyword_io = KeywordIO()