diff --git a/.travis.yml b/.travis.yml index bafd753..077b50f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: required language: python python: - - "2.7" + - "3.7" env: global: diff --git a/deployment/ansible/development/group_vars/all.travis.yml b/deployment/ansible/development/group_vars/all.travis.yml index 01051dc..f1addd6 100644 --- a/deployment/ansible/development/group_vars/all.travis.yml +++ b/deployment/ansible/development/group_vars/all.travis.yml @@ -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: diff --git a/deployment/docker-headless/Dockerfile b/deployment/docker-headless/Dockerfile index 79faad2..030e5d2 100644 --- a/deployment/docker-headless/Dockerfile +++ b/deployment/docker-headless/Dockerfile @@ -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 diff --git a/deployment/docker-headless/docker-entrypoint.sh b/deployment/docker-headless/docker-entrypoint.sh index 8c461b0..46887da 100644 --- a/deployment/docker-headless/docker-entrypoint.sh +++ b/deployment/docker-headless/docker-entrypoint.sh @@ -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 diff --git a/deployment/production/docker/headless/Dockerfile b/deployment/production/docker/headless/Dockerfile index 53abbf5..bab377c 100644 --- a/deployment/production/docker/headless/Dockerfile +++ b/deployment/production/docker/headless/Dockerfile @@ -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 diff --git a/deployment/production/docker/headless/docker-entrypoint.sh b/deployment/production/docker/headless/docker-entrypoint.sh index c0fafb3..0ca7e37 100644 --- a/deployment/production/docker/headless/docker-entrypoint.sh +++ b/deployment/production/docker/headless/docker-entrypoint.sh @@ -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 diff --git a/src/headless/REQUIREMENTS.txt b/src/headless/REQUIREMENTS.txt index 76120b4..f0b5969 100644 --- a/src/headless/REQUIREMENTS.txt +++ b/src/headless/REQUIREMENTS.txt @@ -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 diff --git a/src/headless/celery_app.py b/src/headless/celery_app.py index d7ccb6f..1f5ce50 100644 --- a/src/headless/celery_app.py +++ b/src/headless/celery_app.py @@ -1,5 +1,6 @@ # coding=utf-8 import importlib +from importlib import reload import json import os diff --git a/src/headless/tasks/inasafe_analysis.py b/src/headless/tasks/inasafe_analysis.py index 4ec67eb..0f1439f 100644 --- a/src/headless/tasks/inasafe_analysis.py +++ b/src/headless/tasks/inasafe_analysis.py @@ -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) @@ -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 = ( diff --git a/src/headless/tasks/inasafe_wrapper.py b/src/headless/tasks/inasafe_wrapper.py index 07ca7b3..b2c7176 100644 --- a/src/headless/tasks/inasafe_wrapper.py +++ b/src/headless/tasks/inasafe_wrapper.py @@ -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 diff --git a/src/headless/tasks/test/data/input_layers/buildings.xml b/src/headless/tasks/test/data/input_layers/buildings.xml index fd8ce41..ceaa723 100644 --- a/src/headless/tasks/test/data/input_layers/buildings.xml +++ b/src/headless/tasks/test/data/input_layers/buildings.xml @@ -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>{"exposure_id_field": "exposure_id", "exposure_type_field": "exposure_type"}</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>{"commercial": ["shop"], "education": ["school"], "health": ["hospital"], "government": ["ministry"]}</gco:Dictionary> - </value_map> - <layer_geometry> - <gco:CharacterString>polygon</gco:CharacterString> - </layer_geometry> + <inasafe_fields> + <gco:Dictionary>{"exposure_id_field": "exposure_id", "exposure_type_field": "exposure_type"}</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>{"commercial": ["shop"], "education": ["school"], "health": ["hospital"], "government": ["ministry"]}</gco:Dictionary> + </value_map> + <active_band> + <gco:Integer/> + </active_band> </inasafe> </gmd:supplementalInformation> </gmd:MD_DataIdentification> diff --git a/src/headless/tasks/test/data/input_layers/buildings_geojson.qlr b/src/headless/tasks/test/data/input_layers/buildings_geojson.qlr new file mode 100644 index 0000000..8b0eb2b --- /dev/null +++ b/src/headless/tasks/test/data/input_layers/buildings_geojson.qlr @@ -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> diff --git a/src/headless/tasks/test/data/input_layers/buildings_geojson.xml b/src/headless/tasks/test/data/input_layers/buildings_geojson.xml new file mode 120000 index 0000000..933f876 --- /dev/null +++ b/src/headless/tasks/test/data/input_layers/buildings_geojson.xml @@ -0,0 +1 @@ +buildings.xml \ No newline at end of file diff --git a/src/headless/tasks/test/data/input_layers/population_multi_fields.qml b/src/headless/tasks/test/data/input_layers/population_multi_fields.qml index e34221b..f5a2eb3 100644 --- a/src/headless/tasks/test/data/input_layers/population_multi_fields.qml +++ b/src/headless/tasks/test/data/input_layers/population_multi_fields.qml @@ -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() diff --git a/src/headless/tasks/test/data/input_layers/small_grid.qml b/src/headless/tasks/test/data/input_layers/small_grid.qml index 23b3608..ee5b841 100644 --- a/src/headless/tasks/test/data/input_layers/small_grid.qml +++ b/src/headless/tasks/test/data/input_layers/small_grid.qml @@ -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() diff --git a/src/headless/tasks/test/helpers.py b/src/headless/tasks/test/helpers.py index 48b7224..e9f9b2d 100644 --- a/src/headless/tasks/test/helpers.py +++ b/src/headless/tasks/test/helpers.py @@ -24,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') diff --git a/src/headless/tasks/test/test_generate_report.py b/src/headless/tasks/test/test_generate_report.py index ecf7ac8..366985e 100644 --- a/src/headless/tasks/test/test_generate_report.py +++ b/src/headless/tasks/test/test_generate_report.py @@ -3,7 +3,6 @@ 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 ( @@ -38,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.""" diff --git a/src/headless/tasks/test/test_run_analysis.py b/src/headless/tasks/test/test_run_analysis.py index acb94d9..1be76e4 100644 --- a/src/headless/tasks/test/test_run_analysis.py +++ b/src/headless/tasks/test/test_run_analysis.py @@ -2,7 +2,6 @@ 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 ( @@ -102,9 +101,6 @@ def test_run_multi_exposure_analysis(self): # 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.""" diff --git a/src/headless/tasks/test/test_subsequent_run.py b/src/headless/tasks/test/test_subsequent_run.py index 138f87b..33a2fa2 100644 --- a/src/headless/tasks/test/test_subsequent_run.py +++ b/src/headless/tasks/test/test_subsequent_run.py @@ -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( diff --git a/src/headless/utils.py b/src/headless/utils.py index 8ff94b7..8bd4153 100644 --- a/src/headless/utils.py +++ b/src/headless/utils.py @@ -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()