Skip to content

Commit 4b4726c

Browse files
committedDec 19, 2014
Merge pull request #1740 from geopython/MetaSearch-core-merge-no-tests
merge MetaSearch into QGIS core
2 parents 984324b + e4a8fd8 commit 4b4726c

File tree

10 files changed

+483
-36
lines changed

10 files changed

+483
-36
lines changed
 

‎python/plugins/MetaSearch/AUTHORS.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- Tom Kralidis <tomkralidis@gmail.com>
2+
- Alexander Bruy <alexander.bruy@gmail.com>
3+
- Maxim Dubinin <sim@gis-lab.info>
4+
- Angelos Tzotsos <gcpp.kalxas@gmail.com>
5+
- Richard Duivenvoorde <richard@webmapper.net>

‎python/plugins/MetaSearch/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
FILE(GLOB PY_FILES *.py)
2-
PLUGIN_INSTALL(MetaSearch . ${PY_FILES} metadata.txt)
1+
FILE(GLOB PY_FILES __init__.py link_types.py plugin.py util.py)
2+
PLUGIN_INSTALL(MetaSearch . ${PY_FILES} metadata.txt LICENSE.txt)
33

44
FOREACH(dir ui dialogs images resources resources/templates)
55
FILE(GLOB _items ${dir}/*)

‎python/plugins/MetaSearch/LICENSE.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (C) 2010 NextGIS (http://nextgis.ru),
2+
Alexander Bruy (alexander.bruy@gmail.com),
3+
Maxim Dubinin (sim@gis-lab.info),
4+
5+
Copyright (C) 2014 Tom Kralidis (tomkralidis@gmail.com)
6+
7+
This source is free software; you can redistribute it and/or modify it under
8+
the terms of the GNU General Public License as published by the Free
9+
Software Foundation; either version 2 of the License, or (at your option)
10+
any later version.
11+
12+
This code is distributed in the hope that it will be useful, but WITHOUT ANY
13+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15+
details.
16+
17+
You should have received a copy of the GNU General Public License along
18+
with this program; if not, write to the Free Software Foundation, Inc.,
19+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

‎python/plugins/MetaSearch/dialogs/maindialog.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def __init__(self, iface):
8383
# form inputs
8484
self.startfrom = 0
8585
self.maxrecords = 10
86+
self.timeout = 10
8687
self.constraints = []
8788

8889
# Servers tab
@@ -416,6 +417,9 @@ def search(self):
416417
self.startfrom = 0
417418
self.maxrecords = self.spnRecords.value()
418419

420+
# set timeout
421+
self.timeout = self.spnTimeout.value()
422+
419423
# bbox
420424
minx = self.leWest.text()
421425
miny = self.leSouth.text()
@@ -726,7 +730,7 @@ def show_metadata(self):
726730

727731
try:
728732
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
729-
cat = CatalogueServiceWeb(self.catalog_url)
733+
cat = CatalogueServiceWeb(self.catalog_url, timeout=self.timeout)
730734
cat.getrecordbyid(
731735
[self.catalog.records[identifier].identifier])
732736
except ExceptionReport, err:
@@ -798,7 +802,8 @@ def _get_csw(self):
798802
# connect to the server
799803
try:
800804
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
801-
self.catalog = CatalogueServiceWeb(self.catalog_url)
805+
self.catalog = CatalogueServiceWeb(self.catalog_url,
806+
timeout=self.timeout)
802807
return True
803808
except ExceptionReport, err:
804809
msg = self.tr('Error connecting to service: %s') % err

‎python/plugins/MetaSearch/metadata.txt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22
name=MetaSearch Catalogue Client
33
description=MetaSearch is a QGIS plugin to interact with metadata catalogue services (CSW).
44
category=Web
5-
version=0.3.1
5+
version=0.3.2
66
qgisMinimumVersion=2.0
77
icon=images/MetaSearch.png
88
author=Tom Kralidis
99
email=tomkralidis@gmail.com
1010
tags=web,catalogue,service,metadata,csw
11-
homepage=http://geopython.github.io/MetaSearch
12-
tracker=https://github.com/geopython/MetaSearch/issues
13-
repository=https://github.com/geopython/MetaSearch.git
11+
homepage=https://hub.qgis.org/projects/MetaSearch
12+
tracker=https://hub.qgis.org/projects/MetaSearch/issues
13+
repository=https://github.com/qgis/QGIS/tree/master/python/plugins/MetaSearch
1414
experimental=False
1515
deprecated=False
16-
changelog=Version 0.3.1 (2014-09-03)
17-
- fix packaging and translation
16+
changelog=Version 0.3.2 (2014-11-04)
17+
- fix message formatting
18+
- add server timeout as plugin setting
19+
- bump OWSLib dependency

‎python/plugins/MetaSearch/pavement.py

Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
# -*- coding: utf-8 -*-
2+
###############################################################################
3+
#
4+
# Copyright (C) 2014 Tom Kralidis (tomkralidis@gmail.com)
5+
#
6+
# This source is free software; you can redistribute it and/or modify it under
7+
# the terms of the GNU General Public License as published by the Free
8+
# Software Foundation; either version 2 of the License, or (at your option)
9+
# any later version.
10+
#
11+
# This code is distributed in the hope that it will be useful, but WITHOUT ANY
12+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14+
# details.
15+
#
16+
# You should have received a copy of the GNU General Public License along
17+
# with this program; if not, write to the Free Software Foundation, Inc.,
18+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19+
#
20+
###############################################################################
21+
22+
import getpass
23+
import os
24+
import shutil
25+
from urllib import urlencode
26+
from urllib2 import urlopen
27+
import xml.etree.ElementTree as etree
28+
import xmlrpclib
29+
import zipfile
30+
31+
from jinja2 import Environment, FileSystemLoader
32+
from paver.easy import (call_task, cmdopts, error, info, needs, options, path,
33+
pushd, sh, task, Bunch)
34+
35+
PLUGIN_NAME = 'MetaSearch'
36+
BASEDIR = os.path.abspath(os.path.dirname(__file__))
37+
USERDIR = os.path.expanduser('~')
38+
39+
options(
40+
base=Bunch(
41+
home=BASEDIR,
42+
docs=path(BASEDIR) / 'docs',
43+
plugin=path('%s/plugin/MetaSearch' % BASEDIR),
44+
ui=path(BASEDIR) / 'plugin' / PLUGIN_NAME / 'ui',
45+
install=path('%s/.qgis2/python/plugins/MetaSearch' % USERDIR),
46+
ext_libs=path('plugin/MetaSearch/ext-libs'),
47+
tmp=path(path('%s/MetaSearch-dist' % USERDIR)),
48+
version=open('VERSION.txt').read().strip()
49+
),
50+
upload=Bunch(
51+
host='plugins.qgis.org',
52+
port=80,
53+
endpoint='plugins/RPC2/'
54+
)
55+
)
56+
57+
58+
@task
59+
def setup():
60+
"""setup plugin dependencies"""
61+
62+
if not os.path.exists(options.base.ext_libs):
63+
sh('pip install -r requirements.txt --target=%s' %
64+
options.base.ext_libs)
65+
66+
67+
@task
68+
def clean():
69+
"""clean environment"""
70+
71+
if os.path.exists(options.base.install):
72+
if os.path.islink(options.base.install):
73+
os.unlink(options.base.install)
74+
else:
75+
shutil.rmtree(options.base.install)
76+
if os.path.exists(options.base.tmp):
77+
shutil.rmtree(options.base.tmp)
78+
if os.path.exists(options.base.ext_libs):
79+
shutil.rmtree(options.base.ext_libs)
80+
with pushd(options.base.docs):
81+
sh('%s clean' % sphinx_make())
82+
for ui_file in os.listdir(options.base.ui):
83+
if ui_file.endswith('.py') and ui_file != '__init__.py':
84+
os.remove(options.base.plugin / 'ui' / ui_file)
85+
os.remove(path(options.base.home) / '%s.pro' % PLUGIN_NAME)
86+
sh('git clean -dxf')
87+
88+
89+
@task
90+
def build_ui_files():
91+
"""build ui files"""
92+
93+
# compile .ui files into Python
94+
for ui_file in os.listdir(options.base.ui):
95+
if ui_file.endswith('.ui'):
96+
ui_file_basename = os.path.splitext(ui_file)[0]
97+
sh('pyuic4 -o %s/ui/%s.py %s/ui/%s.ui' % (options.base.plugin,
98+
ui_file_basename, options.base.plugin, ui_file_basename))
99+
100+
101+
@task
102+
def build_pro_file():
103+
"""create .pro file"""
104+
105+
get_translations()
106+
107+
pyfiles = []
108+
uifiles = []
109+
trfiles = []
110+
111+
for root, dirs, files in os.walk(options.base.plugin / 'dialogs'):
112+
for pfile in files:
113+
if pfile.endswith('.py'):
114+
filepath = os.path.join(root, pfile)
115+
relpath = os.path.relpath(filepath, BASEDIR)
116+
pyfiles.append(relpath)
117+
pyfiles.append(options.base.plugin / 'plugin.py')
118+
119+
for root, dirs, files in os.walk(options.base.plugin / 'ui'):
120+
for pfile in files:
121+
if pfile.endswith('.ui'):
122+
filepath = os.path.join(root, pfile)
123+
relpath = os.path.relpath(filepath, BASEDIR)
124+
uifiles.append(relpath)
125+
126+
locale_dir = options.base.plugin / 'locale'
127+
for loc_dir in os.listdir(locale_dir):
128+
filepath = os.path.join(locale_dir, loc_dir, 'LC_MESSAGES', 'ui.ts')
129+
relpath = os.path.relpath(filepath, BASEDIR)
130+
trfiles.append(relpath)
131+
132+
with open('%s.pro' % PLUGIN_NAME, 'w') as pro_file:
133+
pro_file.write('SOURCES=%s\n' % ' '.join(pyfiles))
134+
pro_file.write('FORMS=%s\n' % ' '.join(uifiles))
135+
pro_file.write('TRANSLATIONS=%s\n' % ' '.join(trfiles))
136+
137+
138+
@task
139+
@needs(['build_pro_file'])
140+
def extract_messages():
141+
"""generate .pot/.ts files from sources"""
142+
143+
# generate UI .ts file
144+
sh('pylupdate4 -noobsolete MetaSearch.pro')
145+
146+
# generate .po file from plugin templates
147+
env = Environment(extensions=['jinja2.ext.i18n'],
148+
loader=FileSystemLoader(options.base.plugin))
149+
150+
msg_strings = []
151+
for tfile in ['service_metadata.html', 'record_metadata_dc.html']:
152+
html_file = options.base.plugin / 'resources/templates' / tfile
153+
for msg in env.extract_translations(open(html_file).read()):
154+
if msg[2] not in msg_strings:
155+
msg_strings.append(msg[2])
156+
157+
po_file = options.base.plugin / 'locale/en/LC_MESSAGES/templates.po'
158+
with open(po_file, 'w') as po_file_obj:
159+
po_file_obj.write(
160+
'\nmsgid ""\n'
161+
'msgstr ""\n'
162+
'"Project-Id-Version: MetaSearch 0.1-dev\\n"\n'
163+
'"Report-Msgid-Bugs-To: \\n"\n'
164+
'"POT-Creation-Date: 2014-02-25 12:58-0500\\n"\n'
165+
'"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"\n'
166+
'"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"\n'
167+
'"Language-Team: LANGUAGE <LL@li.org>\\n"\n'
168+
'"MIME-Version: 1.0\\n"\n'
169+
'"Content-Type: text/plain; charset=UTF-8\\n"\n'
170+
'"Content-Transfer-Encoding: 8bit\\n"\n\n')
171+
for msg in msg_strings:
172+
po_file_obj.write('msgid "%s"\nmsgstr ""\n\n' % msg)
173+
174+
# generate docs .po files
175+
with pushd(options.base.docs):
176+
sh('make gettext')
177+
locales_arg = ''
178+
for lang in os.listdir('locale'):
179+
locales_arg = '%s -l %s' % (locales_arg, lang)
180+
sh('sphinx-intl update -p _build/locale %s' % locales_arg)
181+
182+
183+
@task
184+
@needs('build_pro_file')
185+
def compile_messages():
186+
"""generate .qm/.po files"""
187+
188+
# generate UI .qm file
189+
sh('lrelease MetaSearch.pro')
190+
191+
# generate all .mo files
192+
locales = options.base.plugin / 'locale'
193+
194+
for locale_dir in os.listdir(locales):
195+
with pushd(locales / locale_dir):
196+
for filename in os.listdir('LC_MESSAGES'):
197+
if filename.endswith('.po'):
198+
with pushd('LC_MESSAGES'):
199+
sh('msgfmt %s -o %s' %
200+
(filename, filename.replace('.po', '.mo')))
201+
202+
# generate docs .mo files
203+
with pushd(options.base.docs):
204+
sh('sphinx-intl build')
205+
206+
207+
@task
208+
def install():
209+
"""install plugin into user QGIS environment"""
210+
211+
plugins_dir = path(USERDIR) / '.qgis2/python/plugins'
212+
213+
if os.path.exists(options.base.install):
214+
if os.path.islink(options.base.install):
215+
os.unlink(options.base.install)
216+
else:
217+
shutil.rmtree(options.base.install)
218+
219+
if not os.path.exists(plugins_dir):
220+
raise OSError('The directory %s does not exist.' % plugins_dir)
221+
if not hasattr(os, 'symlink'):
222+
shutil.copytree(options.base.plugin, options.base.install)
223+
elif not os.path.exists(options.base.install):
224+
os.symlink(options.base.plugin, options.base.install)
225+
226+
227+
@task
228+
def refresh_docs():
229+
"""Build sphinx docs from scratch"""
230+
231+
get_translations()
232+
make = sphinx_make()
233+
with pushd(options.base.docs):
234+
sh('%s clean' % make)
235+
sh('sphinx-intl build')
236+
for lang in os.listdir(options.base.docs / 'locale'):
237+
builddir = '%s/_build/%s' % (options.base.docs, lang)
238+
sh('%s -e SPHINXOPTS="-D language=\'%s\'" -e BUILDDIR="%s" html' %
239+
(make, lang, builddir))
240+
241+
242+
@task
243+
@needs('refresh_docs')
244+
def publish_docs():
245+
"""this script publish Sphinx outputs to github pages"""
246+
247+
tempdir = options.base.tmp / 'tempdocs'
248+
249+
sh('git clone git@github.com:geopython/MetaSearch.git %s' %
250+
tempdir)
251+
with pushd(tempdir):
252+
sh('git checkout gh-pages')
253+
# copy English to root
254+
sh('cp -rp %s/docs/_build/en/html/* .' % options.base.home)
255+
# copy all other languages to their own dir
256+
for lang in os.listdir(options.base.docs / '_build'):
257+
if lang != 'en':
258+
# point all resources to english
259+
for res in ['_static', '_sources', '_images']:
260+
sh('rm -fr %s/docs/_build/%s/html/%s' %
261+
(options.base.home, lang, res))
262+
# update .html files to point to English
263+
for dfile in os.listdir(options.base.docs /
264+
'_build/%s/html' % lang):
265+
if dfile.endswith('.html'):
266+
lfile = options.base.docs / '_build/%s/html/%s' % \
267+
(lang, dfile)
268+
source = open(lfile).read()
269+
for res in ['_static', '_sources', '_images']:
270+
source = source.replace(res, '../%s' % res)
271+
with open(lfile, 'w') as fhl:
272+
fhl.write(source)
273+
sh('mkdir -p %s' % lang)
274+
sh('cp -rp %s/docs/_build/%s/html/* %s' %
275+
(options.base.home, lang, lang))
276+
sh('git add .')
277+
sh('git commit -am "update live docs [ci skip]"')
278+
sh('git push origin gh-pages')
279+
280+
tempdir.rmtree()
281+
282+
283+
@task
284+
@needs('setup', 'extract_messages', 'compile_messages')
285+
def package():
286+
"""create zip file of plugin"""
287+
288+
package_file = get_package_filename()
289+
290+
if not os.path.exists(options.base.tmp):
291+
options.base.tmp.mkdir()
292+
if os.path.exists(package_file):
293+
os.unlink(package_file)
294+
with zipfile.ZipFile(package_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
295+
for root, dirs, files in os.walk(options.base.plugin):
296+
for file_add in files:
297+
if file_add.endswith('.pyc'):
298+
continue
299+
filepath = os.path.join(root, file_add)
300+
relpath = os.path.relpath(filepath,
301+
os.path.join(BASEDIR, 'plugin'))
302+
zipf.write(filepath, relpath)
303+
return package_file # return name of created zipfile
304+
305+
306+
@task
307+
@cmdopts([
308+
('user=', 'u', 'OSGeo userid'),
309+
])
310+
def upload():
311+
"""upload package zipfile to server"""
312+
313+
user = options.get('user', False)
314+
if not user:
315+
raise ValueError('OSGeo userid required')
316+
317+
password = getpass.getpass('Enter your password: ')
318+
if password.strip() == '':
319+
raise ValueError('password required')
320+
321+
call_task('package')
322+
323+
zipf = get_package_filename()
324+
325+
url = 'http://%s:%s@%s:%d/%s' % (user, password, options.upload.host,
326+
options.upload.port,
327+
options.upload.endpoint)
328+
329+
info('Uploading to http://%s/%s' % (options.upload.host,
330+
options.upload.endpoint))
331+
332+
server = xmlrpclib.ServerProxy(url, verbose=False)
333+
334+
try:
335+
with open(zipf) as zfile:
336+
plugin_id, version_id = \
337+
server.plugin.upload(xmlrpclib.Binary(zfile.read()))
338+
info('Plugin ID: %s', plugin_id)
339+
info('Version ID: %s', version_id)
340+
except xmlrpclib.Fault, err:
341+
error('ERROR: fault error')
342+
error('Fault code: %d', err.faultCode)
343+
error('Fault string: %s', err.faultString)
344+
except xmlrpclib.ProtocolError, err:
345+
error('Error: Protocol error')
346+
error("%s : %s", err.errcode, err.errmsg)
347+
if err.errcode == 403:
348+
error('Invalid name and password')
349+
350+
351+
@task
352+
def test_default_csw_connections():
353+
"""test that the default CSW connections work"""
354+
355+
relpath = 'resources/connections-default.xml'
356+
csw_connections_xml = options.base.plugin / relpath
357+
358+
csws = etree.parse(csw_connections_xml)
359+
360+
for csw in csws.findall('csw'):
361+
# name = csw.attrib.get('name')
362+
data = {
363+
'service': 'CSW',
364+
'version': '2.0.2',
365+
'request': 'GetCapabilities'
366+
}
367+
values = urlencode(data)
368+
url = '%s?%s' % (csw.attrib.get('url'), values)
369+
content = urlopen(url)
370+
if content.getcode() != 200:
371+
raise ValueError('Bad HTTP status code')
372+
csw_xml = etree.fromstring(content.read())
373+
tag = '{http://www.opengis.net/cat/csw/2.0.2}Capabilities'
374+
if csw_xml.tag != tag:
375+
raise ValueError('root element should be csw:Capabilities')
376+
377+
378+
def sphinx_make():
379+
"""return what command Sphinx is using for make"""
380+
381+
if os.name == 'nt':
382+
return 'make.bat'
383+
return 'make'
384+
385+
386+
def get_package_filename():
387+
"""return filepath of plugin zipfile"""
388+
389+
filename = '%s-%s.zip' % (PLUGIN_NAME, options.base.version)
390+
package_file = '%s/%s' % (options.base.tmp, filename)
391+
return package_file
392+
393+
394+
def get_translations():
395+
"""get Transifex translations"""
396+
397+
sh('tx pull -a')

‎python/plugins/MetaSearch/plugin.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
###############################################################################
2525

2626
import logging
27-
import os
2827

2928
from PyQt4.QtCore import QCoreApplication, QLocale, QSettings, QTranslator
3029
from PyQt4.QtGui import QAction, QIcon
@@ -47,31 +46,6 @@ def __init__(self, iface):
4746
self.dialog = None
4847
self.web_menu = '&MetaSearch'
4948

50-
LOGGER.debug('Setting up i18n')
51-
52-
# TODO: does this work for locales like: pt_BR ?
53-
locale_name = QSettings().value("locale/userLocale")[0:2]
54-
# this one below does not pick up when you load QGIS with --lang param
55-
# locale_name = str(QLocale.system().name()).split('_')[0]
56-
57-
LOGGER.debug('Locale name: %s', locale_name)
58-
59-
# load if exists
60-
tr_file = os.path.join(self.context.ppath, 'locale', locale_name,
61-
'LC_MESSAGES', 'ui.qm')
62-
63-
if os.path.exists(tr_file):
64-
self.translator = QTranslator()
65-
result = self.translator.load(tr_file)
66-
if not result:
67-
msg = 'Failed to load translation: %s' % tr_file
68-
LOGGER.error(msg)
69-
raise RuntimeError(msg)
70-
QCoreApplication.installTranslator(self.translator)
71-
72-
LOGGER.debug(QCoreApplication.translate('MetaSearch',
73-
'Translation loaded: %s' % tr_file))
74-
7549
def initGui(self):
7650
"""startup"""
7751

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Paver
2+
pep8
3+
pylint
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Jinja2==2.7.2
2+
OWSLib==0.8.10
3+
pygments==1.6

‎python/plugins/MetaSearch/ui/maindialog.ui

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,45 @@
480480
</property>
481481
</widget>
482482
</widget>
483+
<widget class="QGroupBox" name="groupBox_4">
484+
<property name="geometry">
485+
<rect>
486+
<x>10</x>
487+
<y>90</y>
488+
<width>571</width>
489+
<height>71</height>
490+
</rect>
491+
</property>
492+
<property name="title">
493+
<string>Server timeout</string>
494+
</property>
495+
<widget class="QSpinBox" name="spnTimeout">
496+
<property name="geometry">
497+
<rect>
498+
<x>60</x>
499+
<y>30</y>
500+
<width>104</width>
501+
<height>26</height>
502+
</rect>
503+
</property>
504+
<property name="value">
505+
<number>10</number>
506+
</property>
507+
</widget>
508+
<widget class="QLabel" name="label_9">
509+
<property name="geometry">
510+
<rect>
511+
<x>170</x>
512+
<y>40</y>
513+
<width>46</width>
514+
<height>13</height>
515+
</rect>
516+
</property>
517+
<property name="text">
518+
<string>seconds</string>
519+
</property>
520+
</widget>
521+
</widget>
483522
</widget>
484523
</widget>
485524
</item>

0 commit comments

Comments
 (0)
Please sign in to comment.