Skip to content

Commit

Permalink
Merge branch 'ghini-1.1' of github.com:Ghini/ghini.desktop into ghini…
Browse files Browse the repository at this point in the history
…-1.1

Conflicts:
	bauble/images/bauble_logo.png
	bauble/images/icon.svg
  • Loading branch information
mfrasca committed Jan 16, 2016
2 parents 03091b5 + b50e667 commit 9458e91
Show file tree
Hide file tree
Showing 33 changed files with 687 additions and 766 deletions.
1 change: 1 addition & 0 deletions README.rst
Expand Up @@ -4,6 +4,7 @@ Ghini
.. image:: https://travis-ci.org/Ghini/ghini.desktop.svg
.. image:: https://img.shields.io/pypi/v/bauble.svg
.. image:: https://coveralls.io/repos/Ghini/ghini.desktop/badge.svg?branch=master&service=github
:target: https://coveralls.io/github/Ghini/ghini.desktop?branch=master

.. image:: https://hosted.weblate.org/widgets/bauble/es/svg-badge.svg
.. image:: https://hosted.weblate.org/widgets/bauble/pt_BR/svg-badge.svg
Expand Down
2 changes: 1 addition & 1 deletion bauble/bauble.glade
Expand Up @@ -159,7 +159,7 @@
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">go-previous</property>
<property name="stock">gtk-go-back</property>
</object>
</child>
</object>
Expand Down
6 changes: 5 additions & 1 deletion bauble/db.py
Expand Up @@ -131,6 +131,9 @@ def after_delete(self, mapper, connection, instance):
self._add('delete', mapper, instance)


registered_tables = {}


class MapperBase(DeclarativeMeta):
"""
MapperBase adds the id, _created and _last_updated columns to all
Expand All @@ -151,6 +154,7 @@ def __init__(cls, classname, bases, dict_):
default=sa.func.now(),
onupdate=sa.func.now())
cls.__mapper_args__ = {'extension': HistoryExtension()}
registered_tables[dict_['__tablename__']] = cls
if 'top_level_count' not in dict_:
cls.top_level_count = lambda x: {classname: 1}
if 'search_view_markup_pair' not in dict_:
Expand Down Expand Up @@ -494,7 +498,7 @@ def __getattr__(self, name):
return n.note
if result == []:
# if nothing was found, do not break the proxy.
return Base.__getattr__(self, name)
return self.__getattribute__(name)
if is_dict:
return dict(result)
return result
Expand Down
Binary file added bauble/images/Ghini-fp.svgz
Binary file not shown.
Binary file added bauble/images/bcm.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added bauble/images/name.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 27 additions & 32 deletions bauble/plugins/abcd/__init__.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2005,2006,2007,2008,2009 Brett Adams <brett@belizebotanic.org>
# Copyright (c) 2012-2015 Mario Frasca <mario@anche.no>
# Copyright (c) 2012-2016 Mario Frasca <mario@anche.no>
#
# This file is part of bauble.classic.
#
Expand All @@ -17,29 +17,23 @@
#
# You should have received a copy of the GNU General Public License
# along with bauble.classic. If not, see <http://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
#
#
# ABCD import/exporter
#

import os
import csv

import gtk

from sqlalchemy import *
from sqlalchemy.orm import *

import bauble
import bauble.db as db
from bauble.error import check, CheckConditionError
from bauble.error import check
import bauble.paths as paths
import bauble.utils as utils
import bauble.pluginmgr as pluginmgr
from bauble.plugins.plants.species_model import Species
from bauble.plugins.garden.plant import Plant
from bauble.plugins.garden.accession import Accession
from bauble.i18n import _
from bauble import prefs

# NOTE: see biocase provider software for reading and writing ABCD data
# files, already downloaded software to desktop
Expand All @@ -60,6 +54,7 @@
# we could have a special case just for generating labels
#


def validate_xml(root):
"""
Validate root against ABCD 2.06 schema
Expand All @@ -68,19 +63,20 @@ def validate_xml(root):
:returns: True or False depending if root validates correctly
"""
schema_file = os.path.join(paths.lib_dir(), 'plugins',
'abcd','abcd_2.06.xsd')
'abcd', 'abcd_2.06.xsd')
xmlschema_doc = etree.parse(schema_file)
abcd_schema = etree.XMLSchema(xmlschema_doc)
return abcd_schema.validate(root)


# TODO: this function needs to be renamed since we now check an object in
# the list is an Accession them we use the accession data as the UnitID, else
# we treat it as a Plant...using plants is necessary for things like making
# labels but most likely accessions are wanted if we're exchanging data, the
# only problem is that accessions don't keep status, like dead, etc.

def verify_institution(institution):
test = lambda x: x != '' and x != None
test = lambda x: x != '' and x is not None
return test(institution.name) and \
test(institution.technical_contact) and \
test(institution.email) and test(institution.contact) and \
Expand All @@ -102,7 +98,7 @@ def ABCDElement(parent, name, text=None, attrib=None):
if attrib is None:
attrib = {}
el = SubElement(parent, '{%s}%s' % (namespaces['abcd'], name),
nsmap=namespaces, attrib=attrib)
nsmap=namespaces, attrib=attrib)
el.text = text
return el

Expand Down Expand Up @@ -174,7 +170,6 @@ def get_InformalNameString(self):
pass



def create_abcd(decorated_objects, authors=True, validate=True):
"""
:param objects: a list/tuple of objects that implement the ABCDDecorator
Expand All @@ -187,8 +182,8 @@ def create_abcd(decorated_objects, authors=True, validate=True):
import bauble.plugins.garden.institution as institution
inst = institution.Institution()
if not verify_institution(inst):
msg = _('Some or all of the information about your institution or ' \
'business is not complete. Please make sure that the ' \
msg = _('Some or all of the information about your institution or '
'business is not complete. Please make sure that the '
'Name, Technical Contact, Email, Contact and Institution '
'Code fields are filled in.')
utils.message_dialog(msg)
Expand All @@ -213,7 +208,7 @@ def create_abcd(decorated_objects, authors=True, validate=True):

# TODO: need to get the localized language
representation = ABCDElement(description, 'Representation',
attrib={'language': 'en'})
attrib={'language': 'en'})
revision = ABCDElement(metadata, 'RevisionData')
ABCDElement(revision, 'DateModified', text='2001-03-01T00:00:00')
title = ABCDElement(representation, 'Title', text='TheTitle')
Expand Down Expand Up @@ -249,12 +244,12 @@ def create_abcd(decorated_objects, authors=True, validate=True):

scientific_name = ABCDElement(taxon_identified, 'ScientificName')
ABCDElement(scientific_name, 'FullScientificNameString',
text=obj.get_FullScientificNameString(authors))
text=obj.get_FullScientificNameString(authors))

name_atomised = ABCDElement(scientific_name, 'NameAtomised')
botanical = ABCDElement(name_atomised, 'Botanical')
ABCDElement(botanical, 'GenusOrMonomial',
text=obj.get_GenusOrMonomial())
text=obj.get_GenusOrMonomial())
ABCDElement(botanical, 'FirstEpithet', text=obj.get_FirstEpithet())
author_team = obj.get_AuthorTeam()
if author_team is not None:
Expand All @@ -270,7 +265,7 @@ def create_abcd(decorated_objects, authors=True, validate=True):
result = ABCDElement(identification, 'Result')
taxon_identified = ABCDElement(result, 'TaxonIdentified')
ABCDElement(taxon_identified, 'InformalNameString',
text=vernacular_name)
text=vernacular_name)

# add all the extra non standard elements
obj.extra_elements(unit)
Expand All @@ -290,22 +285,21 @@ def create_abcd(decorated_objects, authors=True, validate=True):
return ElementTree(datasets)



class ABCDExporter(object):
"""
Export Plants to an ABCD file.
"""

def start(self, filename=None, plants=None):
if filename == None: # no filename, ask the user
if filename is None: # no filename, ask the user
d = gtk.FileChooserDialog(_("Choose a file to export to..."), None,
gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT,
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
response = d.run()
filename = d.get_filename()
d.destroy()
if response != gtk.RESPONSE_ACCEPT or filename == None:
if response != gtk.RESPONSE_ACCEPT or filename is None:
return

if plants:
Expand All @@ -314,27 +308,26 @@ def start(self, filename=None, plants=None):
nplants = db.Session().query(Plant).count()

if nplants > 3000:
msg = _('You are exporting %(nplants)s plants to ABCD format. ' \
'Exporting this many plants may take several minutes. '\
msg = _('You are exporting %(nplants)s plants to ABCD format. '
'Exporting this many plants may take several minutes. '
'\n\n<i>Would you like to continue?</i>') \
% ({'nplants': nplants})
% ({'nplants': nplants})
if not utils.yes_no_dialog(msg):
return
self.run(filename, plants)


def run(self, filename, plants=None):
if filename == None:
if filename is None:
raise ValueError("filename can not be None")

if os.path.exists(filename) and not os.path.isfile(filename):
raise ValueError("%s exists and is not a a regular file" \
% filename)
raise ValueError("%s exists and is not a a regular file"
% filename)

# if plants is None then export all plants, this could be huge
# TODO: do something about this, like list the number of plants
# to be returned and make sure this is what the user wants
if plants == None:
if plants is None:
plants = db.Session().query(Plant).all()

# TODO: move PlantABCDAdapter, AccessionABCDAdapter and
Expand All @@ -350,6 +343,8 @@ def run(self, filename, plants=None):
if not validate_xml(data):
msg = _("The ABCD file was created but failed to validate "
"correctly against the ABCD standard.")
if prefs.testing:
raise ValueError(msg)
utils.message_dialog(msg, gtk.MESSAGE_WARNING)


Expand All @@ -369,7 +364,7 @@ class ABCDImexPlugin(pluginmgr.Plugin):

try:
import lxml.etree as etree
import lxml._elementpath # put this here sp py2exe picks it up
import lxml._elementpath # put this here sp py2exe picks it up
from lxml.etree import Element, SubElement, ElementTree
except ImportError:
utils.message_dialog(_('The <i>lxml</i> package is required for the '
Expand Down
6 changes: 3 additions & 3 deletions bauble/plugins/garden/accession.py
Expand Up @@ -1181,7 +1181,7 @@ def sp_get_completions(text):
query = self.presenter().session.query(Species).join('genus').\
filter(utils.ilike(Genus.genus, '%s%%' % text)).\
filter(Species.id != self.model.id).\
order_by(Species.sp)
order_by(Species.epithet)
return query

def sp_cell_data_func(col, cell, model, treeiter, data=None):
Expand Down Expand Up @@ -1775,7 +1775,7 @@ def sp_get_completions(text):
and_(Species.genus_id == Genus.id,
or_(ilike(Genus.genus, '%s%%' % text),
ilike(Genus.genus, '%s%%' % genus)))).\
order_by(Species.sp)
order_by(Species.epithet)

def on_select(value):
logger.debug('on select: %s' % value)
Expand Down Expand Up @@ -1910,7 +1910,7 @@ def refresh_id_qual_rank_combo(self):
active = None
if self.model.id_qual_rank == 'genus':
active = it
it = model.append([str(species.sp), u'sp'])
it = model.append([str(species.epithet), u'sp'])
if self.model.id_qual_rank == u'sp':
active = it

Expand Down
2 changes: 2 additions & 0 deletions bauble/plugins/garden/plant.py
Expand Up @@ -450,6 +450,7 @@ class Plant(db.Base, db.Serializable, db.DefiningPictures, db.WithNotes):

# columns
code = Column(Unicode(6), nullable=False)
date_planted = Column(types.Date, nullable=True, default=None)

@validates('code')
def validate_stripping(self, key, value):
Expand Down Expand Up @@ -670,6 +671,7 @@ def restore_state(self):
class PlantEditorPresenter(GenericEditorPresenter):

widget_to_field_map = {'plant_code_entry': 'code',
'plant_date_entry': 'date_planted',
'plant_acc_entry': 'accession',
'plant_loc_comboentry': 'location',
'plant_acc_type_combo': 'acc_type',
Expand Down
2 changes: 1 addition & 1 deletion bauble/plugins/garden/source.py
Expand Up @@ -263,7 +263,7 @@ class Collection(db.Base):
# ITF2 - F18 - Collection Notes
notes = Column(UnicodeText)

geography_id = Column(Integer, ForeignKey('geography.id'))
geography_id = Column(Integer, ForeignKey('gheography.id'))
region = relation(Geography, uselist=False)

source_id = Column(Integer, ForeignKey('source.id'), unique=True)
Expand Down
12 changes: 6 additions & 6 deletions bauble/plugins/garden/test.py
Expand Up @@ -180,10 +180,10 @@ def __init__(self, *args, **kwargs):
def setUp(self):
super(GardenTestCase, self).setUp()
plants_test.setUp_data()
self.family = Family(family=u'Cactaceae')
self.genus = Genus(family=self.family, genus=u'Echinocactus')
self.species = Species(genus=self.genus, sp=u'grusonii')
self.sp2 = Species(genus=self.genus, sp=u'texelensis')
self.family = Family(epithet=u'Cactaceae')
self.genus = Genus(family=self.family, epithet=u'Echinocactus')
self.species = Species(genus=self.genus, epithet=u'grusonii')
self.sp2 = Species(genus=self.genus, epithet=u'texelensis')
self.session.add_all([self.family, self.genus, self.species, self.sp2])
self.session.commit()

Expand Down Expand Up @@ -884,7 +884,7 @@ def __init__(self, *args):

def setUp(self):
super(AccessionQualifiedTaxon, self).setUp()
self.sp3 = Species(genus=self.genus, sp=u'grusonii',
self.sp3 = Species(genus=self.genus, epithet=u'grusonii',
infrasp1_rank=u'var.', infrasp1=u'albispinus')
self.session.add(self.sp3)
self.session.commit()
Expand Down Expand Up @@ -1190,7 +1190,7 @@ def test_editor(self):
"""
raise SkipTest('separate view from presenter, then test presenter')
#donor = self.create(Donor, name=u'test')
sp2 = Species(genus=self.genus, sp=u'species')
sp2 = Species(genus=self.genus, epithet=u'species')
sp2.synonyms.append(self.species)
self.session.add(sp2)
self.session.commit()
Expand Down
25 changes: 5 additions & 20 deletions bauble/plugins/imex/csv_.py
Expand Up @@ -350,11 +350,15 @@ def create_table(table):

# import the tables one at a time, breaking every so often
# so the GUI can update
from bauble.db import registered_tables
for table, filename in reversed(sorted_tables):
if self.__cancel or self.__error:
break
klass = registered_tables.get(table.name)
klassname = klass and klass.__name__ or table.name
msg = _('importing %(table)s table from %(filename)s') \
% {'table': table.name, 'filename': filename}
% {'table': klassname,
'filename': filename}
#log.info(msg)
bauble.task.set_message(msg)
yield # allow progress bar update
Expand Down Expand Up @@ -542,20 +546,6 @@ def do_insert():
traceback.format_exc(),
type=gtk.MESSAGE_ERROR)

# TODO: we don't use the progress dialog any more but we'll leave this
# around to remind us when we support cancelling via the progress statusbar
#
# def _cancel_import(self, *args):
# '''
# called by the progress dialog to cancel the current import
# '''
# msg = _('Are you sure you want to cancel importing?\n\n<i>All '
# 'changes so far will be rolled back.</i>')
# self.__pause = True
# if utils.yes_no_dialog(msg, parent=self.__progress_dialog):
# self.__cancel = True
## self.__pause = False

def _get_filenames(self):
def on_selection_changed(filechooser, data=None):
"""
Expand Down Expand Up @@ -586,8 +576,6 @@ def on_response(self, widget, response, data=None):
logger.debug(response)


# TODO: add support for exporting only specific tables

class CSVExporter(object):

def start(self, path=None):
Expand All @@ -613,10 +601,7 @@ def start(self, path=None):
logger.debug(e)

def __export_task(self, path):
# if not os.path.exists(path):
# raise ValueError("CSVExporter: path does not exist.\n" + path)
filename_template = os.path.join(path, "%s.txt")
# timeout = tasklet.WaitForTimeout(12)
steps_so_far = 0
ntables = 0
for table in db.metadata.sorted_tables:
Expand Down

0 comments on commit 9458e91

Please sign in to comment.