From f5969cf4bd6e478c5a5dedbf570b2bad76949072 Mon Sep 17 00:00:00 2001 From: Christopher Dickinson Date: Mon, 17 Jun 2013 14:11:54 +0200 Subject: [PATCH] BibAuthority: initial release * BibAuthority: New module containing utility functions, configuration and tests. Adds demo records and new index tables. Adds HowTo guides for admin and hacking pages. (closes #1602) * BibIndex: When indexing authority-controlled subfields of bibliographic records, enrich the index with terms from a configurable list of subfield values from the referenced authority record. * BibIndex: When looking for recently changed bibliographic records to re-index for a given authority-controlled subfield, reindex all bibliographic records that contain references towards recently updated authority records, even if the bibliographic records in questions have not been recently updated. * BibFormat: Simple HTML for authority record. Links for browsing between dependent authority and bibliographic records. * Other modules: some modifications in other modules as well. Co-authored-by: Grzegorz Szpura Signed-off-by: Grzegorz Szpura Tested-by: Tibor Simko --- configure.ac | 7 + modules/Makefile.am | 1 + modules/bibauthority/Makefile.am | 21 + modules/bibauthority/bin/Makefile.am | 19 + modules/bibauthority/doc/Makefile.am | 31 + modules/bibauthority/doc/admin/Makefile.am | 24 + .../doc/admin/bibauthority-admin-guide.webdoc | 171 ++ modules/bibauthority/doc/hacking/Makefile.am | 24 + .../doc/hacking/bibauthority-internals.webdoc | 254 +++ modules/bibauthority/lib/Makefile.am | 29 + .../bibauthority/lib/bibauthority_config.py | 149 ++ .../bibauthority/lib/bibauthority_engine.py | 289 +++ .../lib/bibauthority_regression_tests.py | 99 + .../bibauthority/lib/bibauthority_tests.py | 41 + modules/bibauthority/web/Makefile.am | 17 + .../bibfield/lib/bibfield_regression_tests.py | 2 +- .../format_templates/Authority_HTML_brief.bft | 7 + .../Authority_HTML_detailed.bft | 15 + .../Default_HTML_detailed.bft | 2 + .../etc/format_templates/Makefile.am | 4 +- modules/bibformat/etc/output_formats/HB.bfo | 3 +- modules/bibformat/etc/output_formats/HD.bfo | 3 +- .../lib/bibformat_regression_tests.py | 137 +- modules/bibformat/lib/bibformat_web_tests.py | 6 +- modules/bibformat/lib/elements/Makefile.am | 6 +- .../bibformat/lib/elements/bfe_affiliation.py | 42 +- .../lib/elements/bfe_authority_author.py | 73 + .../lib/elements/bfe_authority_control_no.py | 119 ++ .../lib/elements/bfe_authority_institution.py | 205 ++ .../lib/elements/bfe_authority_journal.py | 70 + .../lib/elements/bfe_authority_subject.py | 70 + modules/bibformat/lib/elements/bfe_authors.py | 42 +- .../bibformat/lib/elements/bfe_publisher.py | 21 + modules/bibindex/lib/bibindex_engine.py | 72 +- .../bibindex/lib/bibindex_engine_config.py | 2 + .../lib/bibindex_engine_unit_tests.py | 6 + .../bibindex/lib/bibindex_regression_tests.py | 80 +- .../lib/bibknowledge_regression_tests.py | 2 +- .../bibrank/lib/bibrank_regression_tests.py | 6 +- modules/bibrecord/lib/bibrecord_unit_tests.py | 20 +- modules/bibsort/bin/bibsort.in | 0 modules/miscutil/demo/demobibdata.xml | 1707 +++++++++++++++++ modules/miscutil/demo/democfgdata.sql | 40 + ...invenio_2013_08_20_bibauthority_updates.py | 276 +++ modules/miscutil/sql/tabbibclean.sql | 24 + modules/miscutil/sql/tabcreate.sql | 182 ++ modules/miscutil/sql/tabdrop.sql | 24 + modules/miscutil/sql/tabfill.sql | 39 + modules/webhelp/web/admin/admin.webdoc | 17 + .../web/admin/howto/howto-authority-1.png | Bin 0 -> 44814 bytes .../web/admin/howto/howto-authority-2.png | Bin 0 -> 46413 bytes .../web/admin/howto/howto-authority.webdoc | 99 + .../webhelp/web/admin/howto/howto-marc.webdoc | 10 + modules/webhelp/web/admin/howto/howto.webdoc | 5 + modules/webhelp/web/hacking/hacking.webdoc | 4 + modules/websearch/lib/search_engine.py | 46 +- .../lib/websearch_regression_tests.py | 68 +- modules/websearch/lib/websearchadminlib.py | 3 +- .../webstyle/lib/webstyle_regression_tests.py | 2 +- 59 files changed, 4662 insertions(+), 75 deletions(-) create mode 100755 modules/bibauthority/Makefile.am create mode 100755 modules/bibauthority/bin/Makefile.am create mode 100755 modules/bibauthority/doc/Makefile.am create mode 100755 modules/bibauthority/doc/admin/Makefile.am create mode 100755 modules/bibauthority/doc/admin/bibauthority-admin-guide.webdoc create mode 100755 modules/bibauthority/doc/hacking/Makefile.am create mode 100755 modules/bibauthority/doc/hacking/bibauthority-internals.webdoc create mode 100755 modules/bibauthority/lib/Makefile.am create mode 100755 modules/bibauthority/lib/bibauthority_config.py create mode 100755 modules/bibauthority/lib/bibauthority_engine.py create mode 100755 modules/bibauthority/lib/bibauthority_regression_tests.py create mode 100755 modules/bibauthority/lib/bibauthority_tests.py create mode 100755 modules/bibauthority/web/Makefile.am create mode 100755 modules/bibformat/etc/format_templates/Authority_HTML_brief.bft create mode 100755 modules/bibformat/etc/format_templates/Authority_HTML_detailed.bft create mode 100755 modules/bibformat/lib/elements/bfe_authority_author.py create mode 100755 modules/bibformat/lib/elements/bfe_authority_control_no.py create mode 100755 modules/bibformat/lib/elements/bfe_authority_institution.py create mode 100755 modules/bibformat/lib/elements/bfe_authority_journal.py create mode 100755 modules/bibformat/lib/elements/bfe_authority_subject.py mode change 100755 => 100644 modules/bibsort/bin/bibsort.in create mode 100644 modules/miscutil/lib/upgrades/invenio_2013_08_20_bibauthority_updates.py create mode 100644 modules/webhelp/web/admin/howto/howto-authority-1.png create mode 100644 modules/webhelp/web/admin/howto/howto-authority-2.png create mode 100644 modules/webhelp/web/admin/howto/howto-authority.webdoc diff --git a/configure.ac b/configure.ac index 068dd19be8..0c8ffaf452 100644 --- a/configure.ac +++ b/configure.ac @@ -571,6 +571,13 @@ AC_CONFIG_FILES([config.nice \ modules/bibauthorid/etc/Makefile \ modules/bibauthorid/etc/name_authority_files/Makefile \ modules/bibauthorid/web/Makefile \ + modules/bibauthority/Makefile \ + modules/bibauthority/bin/Makefile \ + modules/bibauthority/doc/Makefile \ + modules/bibauthority/doc/admin/Makefile \ + modules/bibauthority/doc/hacking/Makefile \ + modules/bibauthority/lib/Makefile \ + modules/bibauthority/web/Makefile \ modules/bibcatalog/Makefile \ modules/bibcatalog/doc/Makefile \ modules/bibcatalog/doc/admin/Makefile \ diff --git a/modules/Makefile.am b/modules/Makefile.am index 4aed62b0aa..b29b7885af 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -16,6 +16,7 @@ ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. SUBDIRS = bibauthorid \ + bibauthority \ bibcatalog \ bibcheck \ bibcirculation \ diff --git a/modules/bibauthority/Makefile.am b/modules/bibauthority/Makefile.am new file mode 100755 index 0000000000..cdaf33b348 --- /dev/null +++ b/modules/bibauthority/Makefile.am @@ -0,0 +1,21 @@ +## +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +SUBDIRS = bin doc lib web + +CLEANFILES = *~ diff --git a/modules/bibauthority/bin/Makefile.am b/modules/bibauthority/bin/Makefile.am new file mode 100755 index 0000000000..cb5b948418 --- /dev/null +++ b/modules/bibauthority/bin/Makefile.am @@ -0,0 +1,19 @@ +## +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +CLEANFILES = *~ *.tmp diff --git a/modules/bibauthority/doc/Makefile.am b/modules/bibauthority/doc/Makefile.am new file mode 100755 index 0000000000..442210bc0a --- /dev/null +++ b/modules/bibauthority/doc/Makefile.am @@ -0,0 +1,31 @@ +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +SUBDIRS = admin hacking + +#imgdir = $(localstatedir)/www/img/admin + +#img_DATA = authority-records-1.png \ +# authority-records-2.png + +webdoclibdir = $(libdir)/webdoc/invenio/help + +#webdoclib_DATA = authority-records.webdoc + +#EXTRA_DIST = $(img_DATA) $(webdoclib_DATA) + +CLEANFILES = *~ *.tmp diff --git a/modules/bibauthority/doc/admin/Makefile.am b/modules/bibauthority/doc/admin/Makefile.am new file mode 100755 index 0000000000..abc95460c6 --- /dev/null +++ b/modules/bibauthority/doc/admin/Makefile.am @@ -0,0 +1,24 @@ +## This file is part of Invenio. +## Copyright (C) 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +webdoclibdir = $(libdir)/webdoc/invenio/admin + +webdoclib_DATA = bibauthority-admin-guide.webdoc + +EXTRA_DIST = $(webdoclib_DATA) + +CLEANFILES = *~ *.tmp diff --git a/modules/bibauthority/doc/admin/bibauthority-admin-guide.webdoc b/modules/bibauthority/doc/admin/bibauthority-admin-guide.webdoc new file mode 100755 index 0000000000..627bf2d85c --- /dev/null +++ b/modules/bibauthority/doc/admin/bibauthority-admin-guide.webdoc @@ -0,0 +1,171 @@ +## This file is part of Invenio. +## Copyright (C) 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + + + + + +

Introduction

+

The INVENIO admin can configure the various ways in which authority control works for INVENIO by means of the bibauthority_config.py file. The location and full contents of this configuration file with a commented example configuration are shown at the bottom of this page. Their functionality is explained in the following paragraphs.

+

For examples of how Authority Control works in Invenio from a user's perspective, cf. _(HOWTO Manage Authority Records)_.

+ +

Enforcing types of authority records

+

INVENIO is originally agnostic about the types of authority records it contains. Everything it needs to know about authority records comes, on the one hand, from the authority record types that are contained within the '980__a' fields, and from the configurations related to these types on the other hand. Whereas the '980__a' values are usually edited by the librarians, the INVENIO configuration is the responsibility of the administrator. It is important for librarians and administrators to communicate the exact authority record types as well as the desired functionality relative to the types for the various INVENIO modules.

+ +

BibEdit

+

As admin of an INVENIO instance, you have the possibility of configuring which fields are under authority control. In the “Configuration File Overview” at the end of this page you will find an example of a configuration which will enable the auto-complete functionality for the '100__a', '100__u', '110__a', '130__a', '150__a', '700__a' and '700__u' fields of a bibliographic record in BibEdit. The keys of the “CFG BIBAUTHORITY CONTROLLED FIELDS” dictionary indicate which bibliographic fields are under authority control. If the user types Ctrl-Shift-A while typing within one of these fields, they will propose an auto-complete dropdown list in BibEdit. The user still has the option to enter values manually without use of the drop-down list. The values associated with each key of the dictionary indicate which kind of authority record is to be associated with this field. In the example given, the '100__a' field is associated with the authority record type 'AUTHOR'.

+

The “CFG BIBAUTHORITY AUTOSUGGEST OPTIONS” dictionary gives us the remaining configurations, specific only to the auto-suggest functionality. The value for the 'index' key determines which index type will be used find the authority records that will populate the drop-down with a list of suggestions (cf. the following paragraph on configuring the BibIndex for authority records). The value of the 'insert_here_field' determines which authority record field contains the value that should be used both for constructing the strings of the entries in the drop-down list as well as the value to be inserted directly into the edited subfield if the user clicks on one of the drop-down entries. Finally, the value for the 'disambiguation_fields' key is an ordered list of authority record fields that are used, in the order in which they appear in the list, to disambiguate between authority records with exactly the same value in their 'insert_here_field'.

+ +

BibIndex

+

As an admin of INVENIO, you have the possibility of configuring how indexing works in regards to authority records that are referenced by bibliographic records. When a bibliographic record is indexed for a particular index type, and if that index type contains MARC fields which are under authority control in this particular INVENIO instance (as configured by the, “CFG BIBAUTHORITY CONTROLLED FIELDS” dictionary in the bibauthority_config.py configuration file, mentioned above), then the indexer will include authority record data from specific MARC fields of these authority records in the same index. Which authority record fields are to be used to enrich the indexes for bibliographic records can be configured by the “CFG BIBAUTHORITY AUTHORITY SUBFIELDS TO INDEX” dictionary. In the example below each of the 4 authority record types ('AUTHOR', 'INSTITUTION', 'JOURNAL' and 'SUBJECT') is given a list of authority record MARC fields which are to be scanned for data that is to be included in the indexed terms of the dependent bibliographic records. For the 'AUTHOR' authority records, the example specifies that the values of the fields '100__a', '100__d', '100__q', '400__a', '400__d', and '400__q' (i.e. name, alternative names, and year of birth) should all be included in the data to be indexed for any bibliographic records referencing these authority records in their authority-controlled subfields.

+ +

Configuration File Overview

+

The configuration file for the BibAuthority module can be found at invenio/lib/python/invenio/bibauthority_config.py. Below is a commented example configuration to show how one would typically configure the parameters for BibAuthority. The details of how this works were explained in the paragraphs above.

+
+# CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD
+# the authority record field containing the authority record control number
+CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD = '035__a'
+
+# Separator to be used in control numbers to separate the authority type
+# PREFIX (e.g. "INSTITUTION") from the control_no (e.g. "(CERN)abc123"
+CFG_BIBAUTHORITY_PREFIX_SEP = '|'
+
+# the ('980__a') string that identifies an authority record
+CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_IDENTIFIER = 'AUTHORITY'
+
+# the name of the authority collection.
+# This is needed for searching within the authority record collection.
+CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME = 'Authority Records'
+
+# used in log file and regression tests
+CFG_BIBAUTHORITY_BIBINDEX_UPDATE_MESSAGE = \
+    "Indexing records dependent on modified authority records"
+
+# CFG_BIBAUTHORITY_TYPE_NAMES
+# Some administrators may want to be able to change the names used for the
+# authority types. Although the keys of this dictionary are hard-coded into
+# Invenio, the values are not and can therefore be changed to match whatever
+# values are to be used in the MARC records.
+# WARNING: These values shouldn't be changed on a running INVENIO installation
+# ... since the same values are hard coded into the MARC data,
+# ... including the 980__a subfields of all authority records
+# ... and the $0 subfields of the bibliographic fields under authority control
+CFG_BIBAUTHORITY_TYPE_NAMES = {
+    'INSTITUTION': 'INSTITUTION',
+    'AUTHOR': 'AUTHOR',
+    'JOURNAL': 'JOURNAL',
+    'SUBJECT': 'SUBJECT',
+}
+
+# CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC
+# 1. tells us which bibliographic subfields are under authority control
+# 2. tells us which bibliographic subfields refer to which type of
+# ... authority record (must conform to the keys of CFG_BIBAUTHORITY_TYPE_NAMES)
+CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC = {
+    '100__a': 'AUTHOR',
+    '100__u': 'INSTITUTION',
+    '110__a': 'INSTITUTION',
+    '130__a': 'JOURNAL',
+    '150__a': 'SUBJECT',
+    '260__b': 'INSTITUTION',
+    '700__a': 'AUTHOR',
+    '700__u': 'INSTITUTION',
+}
+
+# CFG_BIBAUTHORITY_CONTROLLED_FIELDS_AUTHORITY
+# Tells us which authority record subfields are under authority control
+# used by autosuggest feature in BibEdit
+# authority record subfields use the $4 field for the control_no (not $0)
+CFG_BIBAUTHORITY_CONTROLLED_FIELDS_AUTHORITY = {
+    '500__a': 'AUTHOR',
+    '510__a': 'INSTITUTION',
+    '530__a': 'JOURNAL',
+    '550__a': 'SUBJECT',
+    '909C1u': 'INSTITUTION', # used in bfe_affiliation
+    '920__v': 'INSTITUTION', # used by FZ Juelich demo data
+}
+
+# constants for CFG_BIBEDIT_AUTOSUGGEST_TAGS
+# CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA for alphabetical sorting
+# ... of drop-down suggestions
+# CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR for sorting of drop-down
+# ... suggestions according to a popularity ranking
+CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA = 'alphabetical'
+CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR = 'by popularity'
+
+# CFG_BIBAUTHORITY_AUTOSUGGEST_CONFIG
+# some additional configuration for auto-suggest drop-down
+# 'field' : which logical or MARC field field to use for this
+# ... auto-suggest type
+# 'insert_here_field' : which authority record field to use
+# ... for insertion into the auto-completed bibedit field
+# 'disambiguation_fields': an ordered list of fields to use
+# ... in case multiple suggestions have the same 'insert_here_field' values
+# TODO: 'sort_by'. This has not been implemented yet !
+CFG_BIBAUTHORITY_AUTOSUGGEST_CONFIG = {
+    'AUTHOR': {
+        'field': 'authorityauthor',
+        'insert_here_field': '100__a',
+        'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR,
+        'disambiguation_fields': ['100__d', '270__m'],
+    },
+    'INSTITUTION':{
+        'field': 'authorityinstitution',
+        'insert_here_field': '110__a',
+        'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA,
+        'disambiguation_fields': ['270__b'],
+    },
+    'JOURNAL':{
+        'field': 'authorityjournal',
+        'insert_here_field': '130__a',
+        'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR,
+    },
+    'SUBJECT':{
+        'field': 'authoritysubject',
+        'insert_here_field': '150__a',
+        'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA,
+    },
+}
+
+# list of authority record fields to index for each authority record type
+# R stands for 'repeatable'
+# NR stands for 'non-repeatable'
+CFG_BIBAUTHORITY_AUTHORITY_SUBFIELDS_TO_INDEX = {
+    'AUTHOR': [
+        '100__a', #Personal Name (NR, NR)
+        '100__d', #Year of birth or other dates (NR, NR)
+        '100__q', #Fuller form of name (NR, NR)
+        '400__a', #(See From Tracing) (R, NR)
+        '400__d', #(See From Tracing) (R, NR)
+        '400__q', #(See From Tracing) (R, NR)
+    ],
+    'INSTITUTION': [
+        '110__a', #(NR, NR)
+        '410__a', #(R, NR)
+    ],
+    'JOURNAL': [
+        '130__a', #(NR, NR)
+        '130__f', #(NR, NR)
+        '130__l', #(NR, NR)
+        '430__a', #(R, NR)
+    ],
+    'SUBJECT': [
+        '150__a', #(NR, NR)
+        '450__a', #(R, NR)
+    ],
+}
+
diff --git a/modules/bibauthority/doc/hacking/Makefile.am b/modules/bibauthority/doc/hacking/Makefile.am new file mode 100755 index 0000000000..6adf37de52 --- /dev/null +++ b/modules/bibauthority/doc/hacking/Makefile.am @@ -0,0 +1,24 @@ +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +webdoclibdir = $(libdir)/webdoc/invenio/hacking + +webdoclib_DATA = bibauthority-internals.webdoc + +EXTRA_DIST = $(webdoclib_DATA) + +CLEANFILES = *~ *.tmp diff --git a/modules/bibauthority/doc/hacking/bibauthority-internals.webdoc b/modules/bibauthority/doc/hacking/bibauthority-internals.webdoc new file mode 100755 index 0000000000..5e1cfdf536 --- /dev/null +++ b/modules/bibauthority/doc/hacking/bibauthority-internals.webdoc @@ -0,0 +1,254 @@ +## -*- mode: html; coding: utf-8; -*- + +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + + + + + +Here you will find a few explanations to the inner workings of BibAuthority. + +

Indexing

+ +

Introduction

+

There are two cases that need special attention when idexing bibliographic +data that contains references to authority records. +The first case is relatively simple and requires the +enriching of bibliographic data with data from authority records +whenever a bibliographic record is being indexed. The second is a bit +more complex, for it requires detecting which bibliographic records +should be re-indexed, based on referenced authority records having +been updated within a given date range.

+ +

Indexing by record ID, by modification date +or by index type

+

First of all, we need to say something about how INVENIO let's the +admin index the data. INVENIO's indexer (BibIndex) is always run as a +task that is executed by INVENIO's scheduler (BibSched). Typically, +this is done either by scheduling a bibindex task from the command +line (manually), or it is part of a periodic task (BibTask) run +directly from BibSched, typically ever 5 minutes. In case it is run +manually, the user has the option of specifying certain record IDs to +be re-indexed, e.g. by specifying ranges of IDs or collections to be +re-indexed. In this case, the selected records are re-indexed whether +or not there were any modifications to the data. Alternatively, the +user can specify a date range, in which case the indexer will search +all the record IDs that have been modified in the selected date range +(by default, the date range would specify all IDs modified since the +last time the indexer was run) and update the index only for those +records. As a third option, the user can specify specific types of +indexes. INVENIO lets you search by different criteria (e.g. 'any +field', 'title', 'author', 'abstract', 'keyword', 'journal', 'year', +'fulltext', …), and each of these criteria corresponds to a +separate index, indexing only the data from the relevant MARC +subfields. Normally, the indexer would update all index types for any +given record ID, but with this third option, the user can limit the +re-indexing to only specific types of indexes if desired. +

+

Note: In reality, INVENIO creates not only 1 but 6 different +indexes per index type. 3 are forward indexes (mapping words, pairs +or phrases to record IDs), 3 are reverse indexes (mapping record IDs +to words, pairs or phrases). The word, pair and phrase indexes are +used for optimizing the searching speed depending on whether the user +searches for words, sub-phrases or entire phrases. These details are +however not relevant for BibAuthority. It simply finds the values to +be indexed and passes them on to the indexer which indexes them as if +it was data coming directly from the bibliographic record.

+ +

Enriching the index data – simple case

+

Once the indexer knows which record ID (and optionally, which +index type) to re-index, including authority data is simply a +question of checking whether the MARC subfields currently being +indexed are under authority control (as specified in the BibAuthority +configuration file). If they are, the indexer must follow the +following (pseudo-)algorithm which will fetch the necessary data from +the referenced authority records:

+

For each +subfield and each record ID currently being re-indexed:

+

If the +subfield is under authority control (→ config file):

+

Get the +type of referenced authority record expected for this field

+

For each +authority record control number found in the corresponding 'XXX__0' +subfields and matching the expected authority record type +(control number prefix):

+

Find the +authority record ID (MARC field '001' control number) corresponding +to the authority record control number (as contained in MARC field +'035' of the authority record)

+

For each +authority record subfield marked as index relevant for the given +$type (→ config file)

+

Add the +values of these subfields to the list of values to be returned and +used for enriching the indexed strings.

+

The strings collected with this algorithm are simply added to the +strings already found by the indexer in the regular bibliographic +record MARC data. Once all the strings are collected, the indexer +goes on with the usual operation, parsing them 3 different times, +once for phrases, once for word-pairs, once for words, which are used +to populate the 6 forward and reverse index tables in the database.

+ +

Updating the index by date range

+

When a bibindex task is created by date range, we are presented +with a more tricky situation which requires a more complex treatment +for it to work properly. As long as the bibindex task is configured +to index by record ID, the simple algorithm described above is enough +to properly index the authority data along with the data from +bibliographic records. This is true also if we use the third option +described above, specifying the particular index type to re-index +with the bibindex task. However, if we launch a bibindex task based +on a date range (by default the date range covers the time since the +last time bibindex task was run on for each of the index types), +bibindex would have no way to know that it must update the index for +a specific bibliographic record if one of the authority records it +references was modified in the specified date range. This would lead +to incomplete indexes.

+

A first idea was to modify the time-stamp for any bibliographic +records as soon as an authority record is modified. Every MARC record +in INVENIO has a 'modification_date' time-stamp which indicates to +the indexer when this record was last modified. If we search for +dependent bibliographic records every time we modify an authority +record, and if we then update the 'modification_date' time-stamp for +each of these dependent bibliographic records, then we can be sure +that the indexer would find and re-index these bibliographic records +as well when indexing by a specified date-range. The problem with +this is a performance problem. If we update the time-stamp for the +bibliographic record, this record will be re-indexed for all of the +mentioned index-types ('author', 'abstract', 'fulltext', etc.), even +though many of them may not cover MARC subfields that are under +authority control, and hence re-indexing them because of a change in +an authority record would be quite useless. In an INVENIO +installation there would typically be 15-30 index-types. Imagine if +you make a change to a 'journal' authority record and only 1 out of +the 20+ index-types is for 'journal'. INVENIO would be re-indexing +20+ index types in stead of only the 1 index type which is relevant +to the the type of the changed authority record.

+

There are two approaches that could solve this problem equally +well. The first approach would require checking – for each +authority record ID which is to be re-indexed – whether there are +any dependent bibliographic records that need to be re-indexed as +well. If done in the right manner, this approach would only re-index +the necessary index types that can contain information from +referenced authority records, and the user could specify the index +type to be re-indexed and the right bibliographic records would still +be found. The second approach works the other way around. In stead of +waiting until we find a recently modified authority record, and then +looking for dependent bibliographic records, we directly launch a +search for bibliographic records containing links to recently updated +authority records and add the record IDs found in this way to the +list of record IDs that need to be re-indexed. +

+

Of the two approaches, the second one was choses based solely upon +considerations of integration into existing INVENIO code. As indexing +in INVENIO currently works, it is more natural and easily readable to +apply the second method than the first.

+

According to the second method, the pseudo-algorithm for finding +the bibliographic record IDs that need to be updated based upon +recently modified authority records in a given date range looks like +this:

+

For each index-type to +re-index:

+

For each subfield +concerned by the index-type:

+

If the subfield is under +authority control (→ config file):

+

Get the type of authority +record associated with this field

+

Get all of the record IDs for +authority records updated in the specified date range.

+

For each record ID

+

Get the authority record +control numbers of this record ID

+

For each authority record +control number

+

Search for and add the +record IDs of bibliographic +records containing this control number (with type in the +prefix) in the 'XXX__0' field of the current subfield +to the list of record IDs to be returned to the caller to be marked +as needing re-indexing.

+



+

+

The record IDs returned in this way are added to the record IDs +that need to be re-indexed (by date range) and then the rest of the +indexing can run as usual.

+ +

Implementation specifics

+

The pseudo-algorithms described above were used as described in +this document, but were not each implemented in a single function. In +order for parts of them to be reusable and also for the various parts +to be properly integrated into existing python modules with similar +functionality (e.g auxiliary search functions were added to INVENIO's +search_engine.py code), the pseudo-algorithms were split up into +multiple nested function calls and integrated where it seemed to best +fit the existing code base of INVENIO. In the case of the +pseudo-algorithm described in “Updating the index by date range”, +the very choice of the algorithm had already depended on how to best +integrate it into the existing code for date-range related indexing.

+ +

Cross-referencing between MARC records

+

In order to reference authority records, we use alphanumeric strings +stored in the $0 subfields of fields that contain other, authority-controlled +subfields as well. The format of these alphanumeric strings for INVENIO is +in part determined by the MARC standard itself, which states that:

+
+ +

Subfield $0 contains the system control +number of the related authority record, or a standard identifier such +as an International Standard Name Identifier (ISNI). The control +number or identifier is preceded by the appropriate MARC Organization +code (for a related authority record) or the Standard Identifier +source code (for a standard identifier scheme), enclosed in +parentheses. See MARC Code List for Organizations for a listing of +organization codes and Standard Identifier Source Codes for code +systems for standard identifiers. Subfield $0 is repeatable for +different control numbers or identifiers.

+
+
+

An example of such a string could be +“(SzGeCERN)abc1234”, where “SzGeCERN” would be the MARC +organization code, +and abc1234 would be the unique identifier for this authority record +within the given organization.

+

Since it is possible for a single field (e.g. field '100') to have +multiple $0 subfields for the same field entry, we need a way to +specify which $0 subfield reference is associated with which other +subfield of the same field entry.

+

For example, imagine that in bibliographic records both '700__a' +('other author' name) as well as '700__u' ('other author' +affiliation) are under authority control. In this case we +would have two '700__0' subfields. Of of them would reference the +author authority record (for the name), +the other one would reference an institution authority record +(for the affiliation). +INVENIO needs some way to know which $0 subfield is associated with +the $a subfield and which one with the $u subfield.

+

We have chosen to solve this in the +following way. Every $0 subfield value will not only contain the +authority record control number, but in addition will be prefixed by +the type of authority record (e.g. 'AUTHOR', 'INSTITUTION', 'JOURNAL' +or 'SUBJECT), separated from the control number by a separator, e.g. ':' (configurable). A possible +$0 subfield value could therefore be: “author:(SzGeCERN)abc1234”. +This will allow INVENIO to know that the $0 subfield containing +“author:(SzGeCERN)abc1234” is associated with the $a subfield +(author's name), containing e.g. “Ellis, John”, whereas the $0 +subfield containing “institution:(SzGeCERN)xyz4321” is associated +with the $u subfield (author's affiliation/institution) of the same +field entry, containing e.g. “CERN”.

diff --git a/modules/bibauthority/lib/Makefile.am b/modules/bibauthority/lib/Makefile.am new file mode 100755 index 0000000000..3045b6ab36 --- /dev/null +++ b/modules/bibauthority/lib/Makefile.am @@ -0,0 +1,29 @@ +## +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +pylibdir = $(libdir)/python/invenio + +pylib_DATA = \ + bibauthority_config.py \ + bibauthority_engine.py \ + bibauthority_tests.py \ + bibauthority_regression_tests.py + +EXTRA_DIST = $(pylib_DATA) + +CLEANFILES = *~ *.tmp *.pyc diff --git a/modules/bibauthority/lib/bibauthority_config.py b/modules/bibauthority/lib/bibauthority_config.py new file mode 100755 index 0000000000..1ca247a5af --- /dev/null +++ b/modules/bibauthority/lib/bibauthority_config.py @@ -0,0 +1,149 @@ +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +# CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD +# the authority record field containing the authority record control number +CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD = '035__a' + +# Separator to be used in control numbers to separate the authority type +# PREFIX (e.g. "INSTITUTION") from the control_no (e.g. "(CERN)abc123" +CFG_BIBAUTHORITY_PREFIX_SEP = '|' + +# the ('980__a') string that identifies an authority record +CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_IDENTIFIER = 'AUTHORITY' + +# the name of the authority collection. +# This is needed for searching within the authority record collection. +CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME = 'Authority Records' + +# used in log file and regression tests +CFG_BIBAUTHORITY_BIBINDEX_UPDATE_MESSAGE = \ + "Indexing records dependent on modified authority records" + +# CFG_BIBAUTHORITY_TYPE_NAMES +# Some administrators may want to be able to change the names used for the +# authority types. Although the keys of this dictionary are hard-coded into +# Invenio, the values are not and can therefore be changed to match whatever +# values are to be used in the MARC records. +# WARNING: These values shouldn't be changed on a running INVENIO installation +# ... since the same values are hard coded into the MARC data, +# ... including the 980__a subfields of all authority records +# ... and the $0 subfields of the bibliographic fields under authority control +CFG_BIBAUTHORITY_TYPE_NAMES = { + 'INSTITUTION': 'INSTITUTION', + 'AUTHOR': 'AUTHOR', + 'JOURNAL': 'JOURNAL', + 'SUBJECT': 'SUBJECT', +} + +# CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC +# 1. tells us which bibliographic subfields are under authority control +# 2. tells us which bibliographic subfields refer to which type of +# ... authority record (must conform to the keys of CFG_BIBAUTHORITY_TYPE_NAMES) +CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC = { + '100__a': 'AUTHOR', + '100__u': 'INSTITUTION', + '110__a': 'INSTITUTION', + '130__a': 'JOURNAL', + '150__a': 'SUBJECT', + '260__b': 'INSTITUTION', + '700__a': 'AUTHOR', + '700__u': 'INSTITUTION', +} + +# CFG_BIBAUTHORITY_CONTROLLED_FIELDS_AUTHORITY +# Tells us which authority record subfields are under authority control +# used by autosuggest feature in BibEdit +# authority record subfields use the $4 field for the control_no (not $0) +CFG_BIBAUTHORITY_CONTROLLED_FIELDS_AUTHORITY = { + '500__a': 'AUTHOR', + '510__a': 'INSTITUTION', + '530__a': 'JOURNAL', + '550__a': 'SUBJECT', + '909C1u': 'INSTITUTION', # used in bfe_affiliation + '920__v': 'INSTITUTION', # used by FZ Juelich demo data +} + +# constants for CFG_BIBEDIT_AUTOSUGGEST_TAGS +# CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA for alphabetical sorting +# ... of drop-down suggestions +# CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR for sorting of drop-down +# ... suggestions according to a popularity ranking +CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA = 'alphabetical' +CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR = 'by popularity' + +# CFG_BIBAUTHORITY_AUTOSUGGEST_CONFIG +# some additional configuration for auto-suggest drop-down +# 'field' : which logical or MARC field field to use for this +# ... auto-suggest type +# 'insert_here_field' : which authority record field to use +# ... for insertion into the auto-completed bibedit field +# 'disambiguation_fields': an ordered list of fields to use +# ... in case multiple suggestions have the same 'insert_here_field' values +# TODO: 'sort_by'. This has not been implemented yet ! +CFG_BIBAUTHORITY_AUTOSUGGEST_CONFIG = { + 'AUTHOR': { + 'field': 'authorityauthor', + 'insert_here_field': '100__a', + 'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR, + 'disambiguation_fields': ['100__d', '270__m'], + }, + 'INSTITUTION':{ + 'field': 'authorityinstitution', + 'insert_here_field': '110__a', + 'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA, + 'disambiguation_fields': ['270__b'], + }, + 'JOURNAL':{ + 'field': 'authorityjournal', + 'insert_here_field': '130__a', + 'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_POPULAR, + }, + 'SUBJECT':{ + 'field': 'authoritysubject', + 'insert_here_field': '150__a', + 'sort_by': CFG_BIBAUTHORITY_AUTOSUGGEST_SORT_ALPHA, + }, +} + +# list of authority record fields to index for each authority record type +# R stands for 'repeatable' +# NR stands for 'non-repeatable' +CFG_BIBAUTHORITY_AUTHORITY_SUBFIELDS_TO_INDEX = { + 'AUTHOR': [ + '100__a', #Personal Name (NR, NR) + '100__d', #Year of birth or other dates (NR, NR) + '100__q', #Fuller form of name (NR, NR) + '400__a', #(See From Tracing) (R, NR) + '400__d', #(See From Tracing) (R, NR) + '400__q', #(See From Tracing) (R, NR) + ], + 'INSTITUTION': [ + '110__a', #(NR, NR) + '410__a', #(R, NR) + ], + 'JOURNAL': [ + '130__a', #(NR, NR) + '130__f', #(NR, NR) + '130__l', #(NR, NR) + '430__a', #(R, NR) + ], + 'SUBJECT': [ + '150__a', #(NR, NR) + '450__a', #(R, NR) + ], +} diff --git a/modules/bibauthority/lib/bibauthority_engine.py b/modules/bibauthority/lib/bibauthority_engine.py new file mode 100755 index 0000000000..ecf77a8799 --- /dev/null +++ b/modules/bibauthority/lib/bibauthority_engine.py @@ -0,0 +1,289 @@ +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +# pylint: disable=C0103 +"""Invenio BibAuthority Engine.""" +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD, \ + CFG_BIBAUTHORITY_AUTHORITY_SUBFIELDS_TO_INDEX,\ + CFG_BIBAUTHORITY_PREFIX_SEP + +import re +from invenio.errorlib import register_exception +from invenio.search_engine import search_pattern, \ + record_exists +from invenio.search_engine_utils import get_fieldvalues +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_IDENTIFIER + +def is_authority_record(recID): + """ + returns whether recID is an authority record + + @param recID: the record id to check + @type recID: int + + @return: True or False + """ + # low-level: don't use possibly indexed logical fields ! + return recID in search_pattern(p='980__a:AUTHORITY') + +def get_dependent_records_for_control_no(control_no): + """ + returns a list of recIDs that refer to an authority record containing + the given control_no. + E.g. if an authority record has the control number + "AUTHOR:(CERN)aaa0005" in its '035__a' subfield, then this function will return all + recIDs of records that contain any 'XXX__0' subfield + containing "AUTHOR:(CERN)aaa0005" + + @param control_no: the control number for an authority record + @type control_no: string + + @return: list of recIDs + """ + # We don't want to return the recID who's control number is control_no + myRecIDs = _get_low_level_recIDs_intbitset_from_control_no(control_no) + # Use search_pattern, since we want to find records from both bibliographic + # as well as authority record collections + return list(search_pattern(p='"' + control_no+'"') - myRecIDs) + +def get_dependent_records_for_recID(recID): + """ + returns a list of recIDs that refer to an authority record containing + the given record ID. + + 'type' is a string (e.g. "AUTHOR") referring to the type of authority record + + @param recID: the record ID for the authority record + @type recID: int + + @return: list of recIDs + """ + recIDs = [] + + # get the control numbers + control_nos = get_control_nos_from_recID(recID) + for control_no in control_nos: + recIDs.extend(get_dependent_records_for_control_no(control_no)) + + return recIDs + +def guess_authority_types(recID): + """ + guesses the type(s) (e.g. AUTHOR, INSTITUTION, etc.) + of an authority record (should only have one value) + + @param recID: the record ID of the authority record + @type recID: int + + @return: list of strings + """ + types = get_fieldvalues(recID, + '980__a', + repetitive_values=False) # remove possible duplicates ! + + #filter out unwanted information + while CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_IDENTIFIER in types: + types.remove(CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_IDENTIFIER) + types = [_type for _type in types if _type.isalpha()] + + return types + +def get_low_level_recIDs_from_control_no(control_no): + """ + returns the list of EXISTING record ID(s) of the authority records + corresponding to the given (INVENIO) MARC control_no + (e.g. 'AUTHOR:(XYZ)abc123') + (NB: the list should normally contain exactly 1 element) + + @param control_no: a (INVENIO) MARC internal control_no to an authority record + @type control_no: string + + @return:: list containing the record ID(s) of the referenced authority record + (should be only one) + """ + # values returned +# recIDs = [] + #check for correct format for control_no +# control_no = "" +# if CFG_BIBAUTHORITY_PREFIX_SEP in control_no: +# auth_prefix, control_no = control_no.split(CFG_BIBAUTHORITY_PREFIX_SEP); +# #enforce expected enforced_type if present +# if (enforced_type is None) or (auth_prefix == enforced_type): +# #low-level search needed e.g. for bibindex +# hitlist = search_pattern(p='980__a:' + auth_prefix) +# hitlist &= _get_low_level_recIDs_intbitset_from_control_no(control_no) +# recIDs = list(hitlist) + + recIDs = list(_get_low_level_recIDs_intbitset_from_control_no(control_no)) + + # filter out "DELETED" recIDs + recIDs = [recID for recID in recIDs if record_exists(recID) > 0] + + # normally there should be exactly 1 authority record per control_number + _assert_unique_control_no(recIDs, control_no) + + # return + return recIDs + +#def get_low_level_recIDs_from_control_no(control_no): +# """ +# Wrapper function for _get_low_level_recIDs_intbitset_from_control_no() +# Returns a list of EXISTING record IDs with control_no +# +# @param control_no: an (INVENIO) MARC internal control number to an authority record +# @type control_no: string +# +# @return: list (in stead of an intbitset) +# """ +# #low-level search needed e.g. for bibindex +# recIDs = list(_get_low_level_recIDs_intbitset_from_control_no(control_no)) +# +# # filter out "DELETED" recIDs +# recIDs = [recID for recID in recIDs if record_exists(recID) > 0] +# +# # normally there should be exactly 1 authority record per control_number +# _assert_unique_control_no(recIDs, control_no) +# +# # return +# return recIDs + +def _get_low_level_recIDs_intbitset_from_control_no(control_no): + """ + returns the intbitset hitlist of ALL record ID(s) of the authority records + corresponding to the given (INVENIO) MARC control number + (e.g. '(XYZ)abc123'), (e.g. from the 035 field) of the authority record. + + Note: This function does not filter out DELETED records!!! The caller + to this function must do this himself. + + @param control_no: an (INVENIO) MARC internal control number to an authority record + @type control_no: string + + @return:: intbitset containing the record ID(s) of the referenced authority record + (should be only one) + """ + #low-level search needed e.g. for bibindex + hitlist = search_pattern( + p=CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD + ":" + + '"' + control_no + '"') + + # return + return hitlist + +def _assert_unique_control_no(recIDs, control_no): + """ + If there are more than one EXISTING recIDs with control_no, log a warning + + @param recIDs: list of record IDs with control_no + @type recIDs: list of int + + @param control_no: the control number of the authority record in question + @type control_no: string + """ + + if len(recIDs) > 1: + error_message = \ + "DB inconsistency: multiple rec_ids " + \ + "(" + ", ".join([str(recID) for recID in recIDs]) + ") " + \ + "found for authority record control number: " + control_no + try: + raise Exception + except: + register_exception(prefix=error_message, + alert_admin=True, + subject=error_message) + +def get_control_nos_from_recID(recID): + """ + get a list of control numbers from the record ID + + @param recID: record ID + @type recID: int + + @return: authority record control number + """ + return get_fieldvalues(recID, CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD, + repetitive_values=False) + +def get_type_from_control_no(control_no): + """simply returns the authority record TYPE prefix contained in + control_no or else an empty string. + + @param control_no: e.g. "AUTHOR:(CERN)abc123" + @type control_no: string + + @return: e.g. "AUTHOR" or "" + """ + + # pattern: any string, followed by the prefix, followed by a parenthesis + pattern = \ + r'.*' + \ + r'(?=' + re.escape(CFG_BIBAUTHORITY_PREFIX_SEP) + re.escape('(') + r')' + m = re.match(pattern, control_no) + return m and m.group(0) or '' + +def guess_main_name_from_authority_recID(recID): + """ + get the main name of the authority record + + @param recID: the record ID of authority record + @type recID: int + + @return: the main name of this authority record (string) + """ + #tags where the main authority record name can be found + main_name_tags = ['100__a', '110__a', '130__a', '150__a'] + main_name = '' + # look for first match only + for tag in main_name_tags: + fieldvalues = get_fieldvalues(recID, tag, repetitive_values=False) + if len(fieldvalues): + main_name = fieldvalues[0] + break + # return first match, if found + return main_name + +def get_index_strings_by_control_no(control_no): + """extracts the index-relevant strings from the authority record referenced by + the 'control_no' parameter and returns it as a list of strings + + @param control_no: a (INVENIO) MARC internal control_no to an authority record + @type control_no: string (e.g. 'author:(ABC)1234') + + @param expected_type: the type of authority record expected + @type expected_type: string, e.g. 'author', 'journal' etc. + + @return: list of index-relevant strings from the referenced authority record + + """ + + from invenio.bibindex_engine import list_union + + #return value + string_list = [] + #1. get recID and authority type corresponding to control_no + rec_IDs = get_low_level_recIDs_from_control_no(control_no) + #2. concatenate and return all the info from the interesting fields for this record + for rec_id in rec_IDs: # in case we get multiple authority records + for tag in CFG_BIBAUTHORITY_AUTHORITY_SUBFIELDS_TO_INDEX.get(get_type_from_control_no(control_no)): + new_strings = get_fieldvalues(rec_id, tag) + string_list = list_union(new_strings, string_list) + #return + return string_list + diff --git a/modules/bibauthority/lib/bibauthority_regression_tests.py b/modules/bibauthority/lib/bibauthority_regression_tests.py new file mode 100755 index 0000000000..318c92af5c --- /dev/null +++ b/modules/bibauthority/lib/bibauthority_regression_tests.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""BibAuthority Regression Test Suite.""" + +__revision__ = "$Id$" + +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD, \ + CFG_BIBAUTHORITY_TYPE_NAMES, \ + CFG_BIBAUTHORITY_PREFIX_SEP + +import unittest +from invenio.testutils import make_test_suite, run_test_suite +from invenio.bibauthority_engine import is_authority_record, \ + get_dependent_records_for_control_no, \ + get_dependent_records_for_recID, \ + guess_authority_types, \ + get_low_level_recIDs_from_control_no, \ + get_control_nos_from_recID, \ + get_index_strings_by_control_no, \ + guess_main_name_from_authority_recID +from invenio.search_engine_utils import get_fieldvalues + +class BibAuthorityEngineTest(unittest.TestCase): + """Check BibEdit web pages whether they are up or not.""" + + def test_bibauthority_is_authority_record(self): + """bibauthority - test is_authority_record()""" + self.assertFalse(is_authority_record(1)) + self.assertTrue(is_authority_record(118)) + + def test_bibauthority_get_dependent_records_for_control_no(self): + """bibauthority - test get_dependent_records_for_control_no()""" + control_no_field = CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD + control_nos = get_fieldvalues(118, control_no_field) + count = 0 + for control_no in control_nos: + count += len(get_dependent_records_for_control_no(control_no)) + self.assertTrue(count) + + def test_bibauthority_get_dependent_records_for_recID(self): + """bibauthority - test get_dependent_records_for_recID()""" + self.assertTrue(len(get_dependent_records_for_recID(118))) + + def test_bibauthority_guess_authority_types(self): + """bibauthority - test guess_authority_types()""" + _type = CFG_BIBAUTHORITY_TYPE_NAMES['AUTHOR'] + self.assertEqual(guess_authority_types(118), [_type]) + + def test_bibauthority_get_low_level_recIDs(self): + """bibauthority - test get_low_level_recIDs_from_control_no()""" + _type = CFG_BIBAUTHORITY_TYPE_NAMES['INSTITUTION'] + control_no = _type + CFG_BIBAUTHORITY_PREFIX_SEP + "(SzGeCERN)iii0002" + recIDs = [121] + self.assertEqual(get_low_level_recIDs_from_control_no(control_no), + recIDs) + + def test_bibauthority_get_control_nos_from_recID(self): + """bibauthority - test get_control_nos_from_recID()""" + self.assertTrue(len(get_control_nos_from_recID(118))) + + def test_bibauthority_guess_main_name(self): + """bibauthority - test guess_main_name_from_authority_recID()""" + recID = 118 + main_name = 'Ellis, John' + self.assertEqual(guess_main_name_from_authority_recID(recID), + main_name) + + def test_authority_record_string_by_control_no(self): + """bibauthority - simple test of get_index_strings_by_control_no()""" + # vars + _type = CFG_BIBAUTHORITY_TYPE_NAMES['AUTHOR'] + control_no = _type + CFG_BIBAUTHORITY_PREFIX_SEP + '(SzGeCERN)aaa0005' + string = 'Ellis, Jonathan Richard' + # run test + self.assertTrue(string in get_index_strings_by_control_no(control_no)) + +TEST_SUITE = make_test_suite( + BibAuthorityEngineTest, +) + +if __name__ == "__main__": + run_test_suite(TEST_SUITE, warn_user=True) diff --git a/modules/bibauthority/lib/bibauthority_tests.py b/modules/bibauthority/lib/bibauthority_tests.py new file mode 100755 index 0000000000..8088c115c1 --- /dev/null +++ b/modules/bibauthority/lib/bibauthority_tests.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""Unit Tests for BibAuthority""" + +from invenio.bibauthority_config import CFG_BIBAUTHORITY_PREFIX_SEP + +import unittest +from invenio.testutils import make_test_suite, run_test_suite +from invenio.bibauthority_engine import get_type_from_control_no + +class TestBibAuthorityEngine(unittest.TestCase): + """Unit tests for bibauthority_engine""" + + def test_split_name_parts(self): + """bibauthority - test get_type_from_authority_id""" + prefix = "JOURNAL" + control_no = "(CERN)abcd1234" # must start with a '(' + self.assertEqual(get_type_from_control_no( + prefix + CFG_BIBAUTHORITY_PREFIX_SEP + control_no), + prefix) + +TEST_SUITE = make_test_suite(TestBibAuthorityEngine) + +if __name__ == "__main__": + run_test_suite(TEST_SUITE) diff --git a/modules/bibauthority/web/Makefile.am b/modules/bibauthority/web/Makefile.am new file mode 100755 index 0000000000..deeeb47324 --- /dev/null +++ b/modules/bibauthority/web/Makefile.am @@ -0,0 +1,17 @@ +## +## This file is part of Invenio. +## Copyright (C) 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. \ No newline at end of file diff --git a/modules/bibfield/lib/bibfield_regression_tests.py b/modules/bibfield/lib/bibfield_regression_tests.py index 0eeef53a92..135ada11e7 100644 --- a/modules/bibfield/lib/bibfield_regression_tests.py +++ b/modules/bibfield/lib/bibfield_regression_tests.py @@ -109,7 +109,7 @@ def setUp(self): def test_records_created(self): """ bibfield - demo file how many records are created """ - self.assertEqual(113, len(self.recs)) + self.assertEqual(141, len(self.recs)) def test_create_record_with_collection_tag(self): """ bibfield - create_record() for single record in collection""" diff --git a/modules/bibformat/etc/format_templates/Authority_HTML_brief.bft b/modules/bibformat/etc/format_templates/Authority_HTML_brief.bft new file mode 100755 index 0000000000..578f801053 --- /dev/null +++ b/modules/bibformat/etc/format_templates/Authority_HTML_brief.bft @@ -0,0 +1,7 @@ +Default HTML brief +Brief Authority HTML format. + + + + + \ No newline at end of file diff --git a/modules/bibformat/etc/format_templates/Authority_HTML_detailed.bft b/modules/bibformat/etc/format_templates/Authority_HTML_detailed.bft new file mode 100755 index 0000000000..944ce73960 --- /dev/null +++ b/modules/bibformat/etc/format_templates/Authority_HTML_detailed.bft @@ -0,0 +1,15 @@ +Authority HTML detailed +Detailed Authority HTML format. + +

Authority Record

+ +
+ + + + + + + + +
\ No newline at end of file diff --git a/modules/bibformat/etc/format_templates/Default_HTML_detailed.bft b/modules/bibformat/etc/format_templates/Default_HTML_detailed.bft index 52ed96823f..314a598a4a 100644 --- a/modules/bibformat/etc/format_templates/Default_HTML_detailed.bft +++ b/modules/bibformat/etc/format_templates/Default_HTML_detailed.bft @@ -41,6 +41,8 @@ suffix="
" /> +

+ diff --git a/modules/bibformat/etc/format_templates/Makefile.am b/modules/bibformat/etc/format_templates/Makefile.am index 5364db2ac9..925d3b0e30 100644 --- a/modules/bibformat/etc/format_templates/Makefile.am +++ b/modules/bibformat/etc/format_templates/Makefile.am @@ -32,7 +32,9 @@ etc_DATA = Default_HTML_captions.bft Picture_HTML_brief.bft \ Video_HTML_brief.bft Video_HTML_detailed.bft \ Basket_Search_Result.bft Default_HTML_meta.bft \ WebAuthorProfile_affiliations_helper.bft DataCite.xsl \ - Default_Mobile_brief.bft Default_Mobile_detailed.bft + Default_Mobile_brief.bft Default_Mobile_detailed.bft \ + Authority_HTML_brief.bft Authority_HTML_detailed.bft + tmpdir = $(prefix)/var/tmp diff --git a/modules/bibformat/etc/output_formats/HB.bfo b/modules/bibformat/etc/output_formats/HB.bfo index 5d3e35bf1e..cc77ec268a 100644 --- a/modules/bibformat/etc/output_formats/HB.bfo +++ b/modules/bibformat/etc/output_formats/HB.bfo @@ -1,8 +1,9 @@ tag 980.a: PICTURE --- Picture_HTML_brief.bft POETRY --- Poetry_HTML_brief.bft +AUTHORITY --- Authority_HTML_brief.bft tag 773.t: Atlantis Times --- Journal_HTML_brief.bft tag 980.a: VIDEO --- Video_HTML_brief.bft -default: Default_HTML_brief.bft \ No newline at end of file +default: Default_HTML_brief.bft diff --git a/modules/bibformat/etc/output_formats/HD.bfo b/modules/bibformat/etc/output_formats/HD.bfo index 172459200a..a40fbf2ee0 100644 --- a/modules/bibformat/etc/output_formats/HD.bfo +++ b/modules/bibformat/etc/output_formats/HD.bfo @@ -1,8 +1,9 @@ tag 980.a: PICTURE --- Picture_HTML_detailed.bft POETRY --- Poetry_HTML_detailed.bft +AUTHORITY --- Authority_HTML_detailed.bft tag 773.t: Atlantis Times --- Journal_HTML_detailed.bft tag 980.a: VIDEO --- Video_HTML_detailed.bft -default: Default_HTML_detailed.bft \ No newline at end of file +default: Default_HTML_detailed.bft diff --git a/modules/bibformat/lib/bibformat_regression_tests.py b/modules/bibformat/lib/bibformat_regression_tests.py index 1ee6028e83..ccf1ac34dc 100644 --- a/modules/bibformat/lib/bibformat_regression_tests.py +++ b/modules/bibformat/lib/bibformat_regression_tests.py @@ -22,13 +22,21 @@ __revision__ = "$Id$" import unittest +import re -from invenio.config import CFG_SITE_URL, CFG_SITE_LANG, CFG_SITE_RECORD +from invenio.config import CFG_SITE_URL, \ + CFG_SITE_LANG, \ + CFG_SITE_RECORD, \ + CFG_SITE_NAME from invenio.testutils import make_test_suite, \ run_test_suite, \ - test_web_page_content + test_web_page_content, \ + get_authenticated_mechanize_browser, \ + make_url from invenio.bibformat import format_record from invenio.bibformat_engine import BibFormatObject +from invenio.bibformat_elements import bfe_authority_author + class BibFormatAPITest(unittest.TestCase): """Check BibFormat API""" @@ -313,6 +321,7 @@ def setUp(self): Ellis, J + AUTHOR|(SzGeCERN)aaa0005 University of Oxford @@ -468,6 +477,126 @@ def test_publinfo_in_html_detailed(self): test_web_page_content(CFG_SITE_URL + '/%s/84' % CFG_SITE_RECORD, expected_text="Nucl. Phys. B: 656 (2003) pp. 23-36")) + +class BibFormatAuthorityRecordsTest(unittest.TestCase): + """Check authority record related functions""" + + def test_brief_output(self): + """bibformat - brief authority record format outputs something""" + self.assertEqual([], + test_web_page_content(CFG_SITE_URL + '/search?cc=Authority+Records&rg=100', + expected_text="Ellis, John, 1946-")) + + def test_detailed_output(self): + """bibformat - brief authority record format outputs some basic information""" + self.assertEqual([], + test_web_page_content(CFG_SITE_URL + '/record/118', + expected_text=["Ellis, Jonathan Richard, 1946-", "Control Number"])) + + def test_empty_string(self): + """bibformat - no empty strings output for variant (4xx) fields""" + class BFO: + lang = 'en' + def fields(self, afield): + if '400' in afield: return [{'a':'A'},{},{'a':'B'}] + else: afield; return [] + + bfo = BFO() + self.assertTrue("Variant" in bfe_authority_author.format_element(bfo, detail='yes')) + self.assertTrue(", , " not in bfe_authority_author.format_element(bfo, detail='yes')) + + +class BibFormatAuthorityRecordsBrowsingTest(unittest.TestCase): + """Tests authority records browsing pre and successor""" + + def setUp(self): + self.re_institution = re.compile(r"Werkstoffsynthese und Herstellverfahren") + self.re_non_compact = re.compile(r"Non-compact supergravity") + self.re_cern_control_number = re.compile(r"INSTITUTION|(SzGeCERN)") + self.re_institution_energy = re.compile(r"Institut für Energieforschung") + + def test_format_authority_browsing_pre_and_successor(self): + """bibformat - test format authority browsing pre and successor""" + base = "/record/140/" + parameters = {} + url = make_url(base, **parameters) + + error_messages = [] + browser = get_authenticated_mechanize_browser("admin", "") + browser.open(url) + link = browser.find_link(text_regex=re.compile("2 dependent records")) + resp = browser.follow_link(link) + link = browser.find_link(text_regex=re.compile("Detailed record"), nr=1) + resp = browser.follow_link(link) + found = self.re_institution.search(resp.read()) + if not found: + error_messages.append("There is no 'Werkstoffsynthese und Herstellverfahren' in html response.") + link = browser.find_link(text_regex=re.compile("1 dependent record")) + resp = browser.follow_link(link) + found = self.re_institution.search(resp.read()) + if not found: + error_messages.append("There is no 'Werkstoffsynthese und Herstellverfahren' in html response.") + self.assertEqual([], error_messages) + + + def test_format_authority_browsing_ellis(self): + """bibformat - test format authority browsing Ellis authority record""" + base = "/record/12/" + parameters = {} + url = make_url(base, **parameters) + + error_messages = [] + browser = get_authenticated_mechanize_browser("admin", "") + browser.open(url) + link = browser.find_link(text_regex=re.compile("Ellis, J")) + resp = browser.follow_link(link) + link = browser.find_link(text_regex=re.compile("Detailed record"), nr=0) + resp = browser.follow_link(link) + link = browser.find_link(text_regex=re.compile("4 dependent records")) + resp = browser.follow_link(link) + found = self.re_non_compact.search(resp.read()) + if not found: + error_messages.append("There is no 'Non-compact supergravity' in html response.") + self.assertEqual([], error_messages) + + + def test_format_authority_browsing_cern(self): + """bibformat - test format authority browsing cern authority record""" + base = "/record/12/" + parameters = {} + url = make_url(base, **parameters) + + error_messages = [] + browser = get_authenticated_mechanize_browser("admin", "") + browser.open(url) + link = browser.find_link(text_regex=re.compile("CERN")) + resp = browser.follow_link(link) + found = self.re_cern_control_number.search(resp.read()) + if not found: + error_messages.append("There is no CERN control number in html response.") + self.assertEqual([], error_messages) + + def test_format_authority_browsing_parent_child(self): + """bibformat - test format authority browsing parent child""" + base = "/record/129/" + parameters = {} + url = make_url(base, **parameters) + + error_messages = [] + browser = get_authenticated_mechanize_browser("admin", "") + browser.open(url) + link = browser.find_link(text_regex=re.compile("Institut für Kernphysik")) + resp = browser.follow_link(link) + link = browser.find_link(text_regex=re.compile("Forschungszentrum Jülich")) + resp = browser.follow_link(link) + link = browser.find_link(text_regex=re.compile("6 dependent records")) + resp = browser.follow_link(link) + found = self.re_institution_energy.search(resp.read()) + if not found: + error_messages.append("There is no 'Institut für Energieforschung' in html response.") + self.assertEqual([], error_messages) + + TEST_SUITE = make_test_suite(BibFormatBibTeXTest, BibFormatDetailedHTMLTest, BibFormatBriefHTMLTest, @@ -478,7 +607,9 @@ def test_publinfo_in_html_detailed(self): BibFormatObjectAPITest, BibFormatTitleFormattingTest, BibFormatISBNFormattingTest, - BibFormatPublInfoFormattingTest) + BibFormatPublInfoFormattingTest, + BibFormatAuthorityRecordsTest, + BibFormatAuthorityRecordsBrowsingTest) if __name__ == "__main__": run_test_suite(TEST_SUITE, warn_user=True) diff --git a/modules/bibformat/lib/bibformat_web_tests.py b/modules/bibformat/lib/bibformat_web_tests.py index 14a509b904..e3f8ce8dfc 100644 --- a/modules/bibformat/lib/bibformat_web_tests.py +++ b/modules/bibformat/lib/bibformat_web_tests.py @@ -27,7 +27,7 @@ class InvenioBibFormatWebTest(InvenioWebTestCase): """BibFormat web tests.""" - + def test_format_many_authors(self): """bibformat - web test format many authors""" @@ -42,7 +42,9 @@ def test_format_many_authors(self): self.page_source_test(expected_text='Show all 315 authors') self.find_element_by_link_text_with_timeout("Show all 315 authors") self.browser.find_element_by_link_text("Show all 315 authors").click() - self.page_source_test(expected_text=['Zobernig, G', 'Hide']) + self.page_source_test(expected_text=['Zobernig, G', 'Hide']) + + TEST_SUITE = make_test_suite(InvenioBibFormatWebTest, ) diff --git a/modules/bibformat/lib/elements/Makefile.am b/modules/bibformat/lib/elements/Makefile.am index 049676d590..47423b9e14 100644 --- a/modules/bibformat/lib/elements/Makefile.am +++ b/modules/bibformat/lib/elements/Makefile.am @@ -37,7 +37,11 @@ pylib_DATA = bfe_field.py bfe_title.py bfe_authors.py bfe_abstract.py bfe_affili bfe_video_platform_sources.py bfe_sciencewise.py bfe_bookmark.py \ bfe_oai_marcxml.py bfe_copyright.py bfe_meta.py bfe_meta_opengraph_image.py \ bfe_meta_opengraph_video.py bfe_webauthorpage_affiliations.py \ - bfe_qrcode.py + bfe_qrcode.py \ + bfe_authority_author.py bfe_authority_institution.py \ + bfe_authority_journal.py bfe_authority_subject.py \ + bfe_authority_control_no.py + tmpdir = $(prefix)/var/tmp/tests_bibformat_elements diff --git a/modules/bibformat/lib/elements/bfe_affiliation.py b/modules/bibformat/lib/elements/bfe_affiliation.py index ee0c8bd12a..f97fd96948 100644 --- a/modules/bibformat/lib/elements/bfe_affiliation.py +++ b/modules/bibformat/lib/elements/bfe_affiliation.py @@ -21,17 +21,47 @@ __revision__ = "$Id$" import cgi +from invenio.config import \ + CFG_SITE_URL, CFG_SITE_NAME +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME + +from invenio.bibauthority_engine import \ + get_low_level_recIDs_from_control_no def format_element(bfo): """ HTML Affiliation display """ - affiliations = bfo.fields('909C1u') - if len(affiliations) > 0: - out = "
" - for affiliation in affiliations: - out += cgi.escape(affiliation) +" " - return out + affiliations = bfo.fields('909C1', repeatable_subfields_p=True) + out = "" + for affiliation_dict in affiliations: + if 'u' in affiliation_dict: + recIDs = [] + affiliation = affiliation_dict['u'][0] + control_nos = affiliation_dict.get('0') + for control_no in control_nos or []: + recIDs.extend(get_low_level_recIDs_from_control_no(control_no)) + affiliation = cgi.escape(affiliation) + if len(recIDs) == 1: + affiliation = '' + affiliation + '' + elif len(recIDs) > 1: + affiliation = '' + affiliation + '' + + out += affiliation + " " + + if out: + return "
" + out + def escape_values(bfo): """ diff --git a/modules/bibformat/lib/elements/bfe_authority_author.py b/modules/bibformat/lib/elements/bfe_authority_author.py new file mode 100755 index 0000000000..8c820b5935 --- /dev/null +++ b/modules/bibformat/lib/elements/bfe_authority_author.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""BibFormat element - Prints author data from an Authority Record. +""" + +import re + +__revision__ = "$Id$" + +def format_element(bfo, detail='no'): + """ Prints the data of an author authority record in HTML. By default prints + brief version. + + @param detail: whether the 'detailed' rather than the 'brief' format + @type detail: 'yes' or 'no' + """ + + from invenio.messages import gettext_set_language + _ = gettext_set_language(bfo.lang) # load the right message language + # return value + out = "" + # local function + def stringify_dict(d): + """ return string composed values in d """ + _str = "" + if 'a' in d: + _str += d['a'] + if 'd' in d: + _str += ", " + d['d'] + return _str or '' + # brief + main_dicts = bfo.fields('100%%') + if len(main_dicts): + main_dict = main_dicts[0] + main = stringify_dict(main_dict) + out += "

" + "" + _("Main %s name") % _("author") + "" + ": " + main + "

" + # detail + if detail.lower() == "yes": + sees = [stringify_dict(see_dict) for see_dict in bfo.fields('400%%')] + sees = filter(None, sees) # fastest way to remove empty ""s + sees = [re.sub(",{2,}",",", x) for x in sees] # prevent ",," + if len(sees): + out += "

" + "" + _("Variant(s)") + "" + ": " + ", ".join(sees) + "

" + see_alsos = [stringify_dict(see_also_dict) for see_also_dict in bfo.fields('500%%')] + see_alsos = filter(None, see_alsos) # fastest way to remove empty ""s + see_alsos = [re.sub(",{2,}",",", x) for x in see_alsos] # prevent ",," + if len(see_alsos): + out += "

" + "" + _("See also") + "" + ": " + ", ".join(see_alsos) + "

" + # return + return out + +def escape_values(bfo): + """ + Called by BibFormat in order to check if output of this element + should be escaped. + """ + return 0 diff --git a/modules/bibformat/lib/elements/bfe_authority_control_no.py b/modules/bibformat/lib/elements/bfe_authority_control_no.py new file mode 100755 index 0000000000..dbc79fd95b --- /dev/null +++ b/modules/bibformat/lib/elements/bfe_authority_control_no.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""BibFormat element - Prints the control number of an Authority Record. +""" + +from invenio.config import CFG_SITE_URL, CFG_SITE_NAME + +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME +from invenio.bibauthority_engine import \ + get_low_level_recIDs_from_control_no, \ + get_dependent_records_for_control_no + +__revision__ = "$Id$" + +def format_element(bfo): + """ Prints the control number of an author authority record in HTML. + By default prints brief version. + + @param brief: whether the 'brief' rather than the 'detailed' format + @type brief: 'yes' or 'no' + """ + + from invenio.messages import gettext_set_language + _ = gettext_set_language(bfo.lang) # load the right message language + + control_nos = [d['a'] for d in bfo.fields('035__')] + control_nos = filter(None, control_nos) # fastest way to remove empty ""s + + control_nos_formatted = [] + for control_no in control_nos: +# recIDs = [] +# types = guess_authority_types(bfo.recID) +# # control_no example: AUTHOR:(CERN)aaa0005" +# control_nos = [(type + CFG_BIBAUTHORITY_PREFIX_SEP + control_no) for type in types] +# for control_no in control_nos: +# recIDs.extend(list(search_pattern(p='"' + control_no + '"'))) + recIDs = get_dependent_records_for_control_no(control_no) + count = len(recIDs) + count_string = str(count) + " dependent records" + + # if we have dependent records, provide a link to them + if count: + prefix_pattern = "" + postfix = "" + url_str = '' + # we have multiple dependent records + if count > 1: + # joining control_nos might be more helpful for the user + # than joining recIDs... or maybe not... +# p_val = '"' + '" or "'.join(control_nos) + '"' # more understandable for the user + p_val = "recid:" + ' or recid:'.join([str(recID) for recID in recIDs]) # more efficient + # include "&c=" parameter for bibliographic records + # and one "&c=" parameter for authority records + url_str = \ + "/search" + \ + "?p=" + p_val + \ + "&c=" + CFG_SITE_NAME + \ + "&c=" + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME + \ + "&sc=1" + \ + "&ln=" + bfo.lang + # we have exactly one dependent record + elif count == 1: + url_str = "/record/" + str(recIDs[0]) + + prefix = prefix_pattern % (url_str) + count_string = prefix + count_string + postfix + #assemble the html and append to list + html_str = control_no + " (" + count_string + ")" + + # check if there are more than one authority record with the same + # control number. If so, warn the user about this inconsistency. + # TODO: hide this warning from unauthorized users + my_recIDs = get_low_level_recIDs_from_control_no(control_no) + if len(my_recIDs) > 1: + url_str = \ + "/search" + \ + "?p=" + "recid:" + 'or recid:'.join([str(_id) for _id in my_recIDs]) + \ + "&c=" + CFG_SITE_NAME + \ + "&c=" + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME + \ + "&sc=1" + \ + "&ln=" + bfo.lang + html_str += \ + ' ' + \ + '(Warning, there is currently ' + \ + 'more than one authority record ' + \ + 'with this Control Number)' + \ + '' + + control_nos_formatted.append(html_str) + + title = "" + _("Control Number(s)") + "" + content = ", ".join(control_nos_formatted) \ + or "Missing !" + + return "

" + title + ": " + content + "

" + +def escape_values(bfo): + """ + Called by BibFormat in order to check if output of this element + should be escaped. + """ + return 0 diff --git a/modules/bibformat/lib/elements/bfe_authority_institution.py b/modules/bibformat/lib/elements/bfe_authority_institution.py new file mode 100755 index 0000000000..94ced00ccb --- /dev/null +++ b/modules/bibformat/lib/elements/bfe_authority_institution.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""BibFormat element - Prints institution data from an Authority Record. +""" + +__revision__ = "$Id$" + +from invenio.config import CFG_SITE_URL +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD, \ + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME + +from invenio.bibauthority_engine import \ + get_control_nos_from_recID, \ + guess_main_name_from_authority_recID +from invenio.search_engine import \ + perform_request_search, \ + get_record + +def format_element(bfo, detail='no'): + """ Prints the data of an institution authority record in HTML. By default prints + brief version. + + @param detail: whether the 'detailed' rather than the 'brief' format + @type detail: 'yes' or 'no' + """ + from invenio.messages import gettext_set_language + _ = gettext_set_language(bfo.lang) # load the right message language + + # return value + out = "" + # brief + main_dicts = bfo.fields('110%%') + if len(main_dicts): + main = main_dicts[0].get('a') or "" + out += "

" + "" + _("Main %s name") % _("institution") + "" + ": " + main + "

" + # detail + if detail.lower() == "yes": + sees = [see_dict['a'] for see_dict in bfo.fields('410%%') if 'a' in see_dict] + sees = filter(None, sees) # fastest way to remove empty ""s + if len(sees): + out += "

" + "" + _("Variant(s)") + "" + ": " + ", ".join(sees) + "

" + see_also_dicts = bfo.fields('510%%') + cc_val = CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME + c_val = "Authority Institution" + record_url_pattern = "/record/" + "%s" + search_url_pattern = "/search?" + \ + "cc=" + "%s" + \ + "&c=" + "%s" + \ + "&p=" + "%s" + \ + "&sc=" + "%s" + link_pattern = "" + '%s' + "" + # populate the first 3 lists + parent_htmls, predecessor_htmls, successor_htmls = \ + get_main_htmls(see_also_dicts, cc_val, c_val, record_url_pattern, + search_url_pattern, link_pattern) + # populate the list of children + child_htmls = \ + get_child_htmls(bfo.recID, cc_val, c_val, record_url_pattern, + link_pattern) + # put it all together + if len(parent_htmls): + out += "

" + "" + _("Parent") + "" + ": " + ", ".join(parent_htmls) + "

" + if len(child_htmls): + out += "

" + "" + _("Children") + "" + ": " + ", ".join(child_htmls) + "

" + if len(predecessor_htmls): + out += "

" + "" + _("Predecessor") + "" + ": " + ", ".join(predecessor_htmls) + "

" + if len(successor_htmls): + out += "

" + "" + _("Successor") + "" + ": " + ", ".join(successor_htmls) + "

" + # return + return out + +def get_main_htmls(see_also_dicts, cc_val, c_val, record_url_pattern, + search_url_pattern, link_pattern): + """parent_htmls, predecessor_htmls, successor_htmls can all be deduced + directly from the metadata of the record""" + # reusable vars + f_val = CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD + sc_val = "1" + parent_htmls = [] + predecessor_htmls = [] + successor_htmls = [] + + # start processing + for see_also_dict in see_also_dicts: + if 'w' in see_also_dict: + # $w contains 'a' for predecessor, 'b' for successor, etc. + w_subfield = see_also_dict.get('w') + # $4 contains control_no of linked authority record + _4_subfield = see_also_dict.get('4') + # $a contains the name of the linked institution + out_string = see_also_dict.get('a') or _4_subfield + # if we have something to display + if out_string: + url = '' + # if we have a control number + if _4_subfield: + p_val = _4_subfield +# if CFG_BIBAUTHORITY_PREFIX_SEP in _4_subfield: +# unused, p_val = _4_subfield.split(CFG_BIBAUTHORITY_PREFIX_SEP); + recIDs = perform_request_search(cc=cc_val, + c=c_val, + p=p_val, + f=f_val) + if len(recIDs) == 1: + url = record_url_pattern % (recIDs[0]) + elif len(recIDs) > 1: + p_val = "recid:" + \ + " or recid:".join([str(r) for r in recIDs]) + url = search_url_pattern % (cc_val, + c_val, + p_val, + sc_val) + # if we found one or multiple records for the control_no, + # make the out_string a clickable url towards those records + if url: + out_string = link_pattern % (url, out_string) + # add the out_string to the appropriate list + if w_subfield == 't': + parent_htmls.append(out_string) + elif w_subfield == 'a': + predecessor_htmls.append(out_string) + elif w_subfield == 'b': + successor_htmls.append(out_string) + # return + return parent_htmls, predecessor_htmls, successor_htmls + +def get_child_htmls(this_recID, cc_val, c_val, record_url_pattern, + link_pattern): + """children aren'r referenced by parents, so we need special treatment to find + them""" + control_nos = get_control_nos_from_recID(this_recID) + for control_no in control_nos: + url = '' + p_val = '510%4:"' + control_no + '" and 510%w:t' + # find a first, fuzzy result set + # narrowing down on a few possible recIDs + recIDs = perform_request_search(cc=cc_val, + c=c_val, + p=p_val) + # now filter to find the ones where the subfield conditions of p_val + # are both true within the exact same field + sf_req = [('w', 't'), ('4', control_no)] + recIDs = filter(lambda x: + match_all_subfields_for_tag(x, '510', sf_req), + recIDs) + # proceed with assembling the html link + child_htmls = [] + for recID in recIDs: + url = record_url_pattern % str(recID) + display = guess_main_name_from_authority_recID(recID) or str(recID) + out_html = link_pattern % (url, display) + child_htmls.append(out_html) + return child_htmls + +def match_all_subfields_for_tag(recID, field_tag, subfields_required=[]): + """ + Tests whether the record with recID has at least one field with 'field_tag' + where all of the required subfields in subfields_required match a subfield + in the given field both in code and value + + @param recID: record ID + @type recID: int + + @param field_tag: a 3 digit code for the field tag code + @type field_tag: string + + @param subfields_required: a list of subfield code/value tuples + @type subfields_required: list of tuples of strings. + same format as in get_record(): + e.g. [('w', 't'), + ('4', 'XYZ123')] + + @return: boolean + """ + rec = get_record(recID) + for field in rec[field_tag]: + subfields_present = field[0] + intersection = set(subfields_present) & set(subfields_required) + if set(subfields_required) == intersection: + return True + return False + +def escape_values(bfo): + """ + Called by BibFormat in order to check if output of this element + should be escaped. + """ + return 0 \ No newline at end of file diff --git a/modules/bibformat/lib/elements/bfe_authority_journal.py b/modules/bibformat/lib/elements/bfe_authority_journal.py new file mode 100755 index 0000000000..ae8f920b4c --- /dev/null +++ b/modules/bibformat/lib/elements/bfe_authority_journal.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""BibFormat element - Prints journal data from an Authority Record. +""" + +import re + +__revision__ = "$Id$" + +def format_element(bfo, detail='no'): + """ Prints the data of a journal authority record in HTML. By default prints + brief version. + + @param detail: whether the 'detailed' rather than the 'brief' format + @type detail: 'yes' or 'no' + """ + from invenio.messages import gettext_set_language + _ = gettext_set_language(bfo.lang) # load the right message language + # return value + out = "" + # local function + def stringify_dict(d): + """ return string composed values in d """ + _str = "" + if 'a' in d: + _str += d['a'] + return _str or '' + # brief + main_dicts = bfo.fields('130%%') + if len(main_dicts): + main_dict = main_dicts[0] + main = stringify_dict(main_dict) + out += "

" + "" + _("Main %s name") % _("journal") + "" + ": " + main + "

" + # detail + if detail.lower() == "yes": + sees = [stringify_dict(see_dict) for see_dict in bfo.fields('430%%')] + sees = filter(None, sees) # fastest way to remove empty ""s + sees = [re.sub(",{2,}",",", x) for x in sees] # prevent ",," + if len(sees): + out += "

" + "" + _("Variant(s)") + "" + ": " + ", ".join(sees) + "

" + see_alsos = [stringify_dict(see_also_dict) for see_also_dict in bfo.fields('530%%')] + see_alsos = filter(None, see_alsos) # fastest way to remove empty ""s + see_alsos = [re.sub(",{2,}",",", x) for x in see_alsos] # prevent ",," + if len(see_alsos): + out += "

" + "" + _("See also") + "" + ": " + ", ".join(see_alsos) + "

" + # return + return out + +def escape_values(bfo): + """ + Called by BibFormat in order to check if output of this element + should be escaped. + """ + return 0 diff --git a/modules/bibformat/lib/elements/bfe_authority_subject.py b/modules/bibformat/lib/elements/bfe_authority_subject.py new file mode 100755 index 0000000000..3e9cc6b19c --- /dev/null +++ b/modules/bibformat/lib/elements/bfe_authority_subject.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +"""BibFormat element - Prints subject data from an Authority Record. +""" + +import re + +__revision__ = "$Id$" + +def format_element(bfo, detail='no'): + """ Prints the data of a subject authority record in HTML. By default prints + brief version. + + @param detail: whether the 'detailed' rather than the 'brief' format + @type detail: 'yes' or 'no' + """ + from invenio.messages import gettext_set_language + _ = gettext_set_language(bfo.lang) # load the right message language + # return value + out = "" + # local function + def stringify_dict(d): + """ return string composed values in d """ + _str = "" + if 'a' in d: + _str += d['a'] + return _str or '' + # brief + main_dicts = bfo.fields('150%%') + if len(main_dicts): + main_dict = main_dicts[0] + main = stringify_dict(main_dict) + out += "

" + "" + _("Main %s name") % _("subject") + "" + ": " + main + "

" + # detail + if detail.lower() == "yes": + sees = [stringify_dict(see_dict) for see_dict in bfo.fields('450%%')] + sees = filter(None, sees) # fastest way to remove empty ""s + sees = [re.sub(",{2,}",",", x) for x in sees] # prevent ",," + if len(sees): + out += "

" + "" + _("Variant(s)") + "" + ": " + ", ".join(sees) + "

" + see_alsos = [stringify_dict(see_also_dict) for see_also_dict in bfo.fields('550%%')] + see_alsos = filter(None, see_alsos) # fastest way to remove empty ""s + see_alsos = [re.sub(",{2,}",",", x) for x in see_alsos] # prevent ",," + if len(see_alsos): + out += "

" + "" + _("See also") + "" + ": " + ", ".join(see_alsos) + "

" + # return + return out + +def escape_values(bfo): + """ + Called by BibFormat in order to check if output of this element + should be escaped. + """ + return 0 diff --git a/modules/bibformat/lib/elements/bfe_authors.py b/modules/bibformat/lib/elements/bfe_authors.py index c0e309f919..7389c781a5 100644 --- a/modules/bibformat/lib/elements/bfe_authors.py +++ b/modules/bibformat/lib/elements/bfe_authors.py @@ -25,6 +25,12 @@ from cgi import escape from invenio.config import CFG_SITE_URL from invenio.messages import gettext_set_language +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME, \ + CFG_BIBAUTHORITY_TYPE_NAMES, \ + CFG_BIBAUTHORITY_PREFIX_SEP +from invenio.bibauthority_engine import \ + get_low_level_recIDs_from_control_no def format_element(bfo, limit, separator=' ; ', extension='[...]', @@ -56,12 +62,26 @@ def format_element(bfo, limit, separator=' ; ', _ = gettext_set_language(bfo.lang) # load the right message language authors = [] - authors_1 = bfo.fields('100__') - authors_2 = bfo.fields('700__') + authors_1 = bfo.fields('100__', repeatable_subfields_p=True) + authors_2 = bfo.fields('700__', repeatable_subfields_p=True) authors.extend(authors_1) authors.extend(authors_2) + # make unique string per key + for author in authors: + if 'a' in author: + author['a'] = author['a'][0] + if 'u' in author: + author['u'] = author['u'][0] + pattern = '%s' + CFG_BIBAUTHORITY_PREFIX_SEP + "(" + for control_no in author.get('0', []): + if pattern % (CFG_BIBAUTHORITY_TYPE_NAMES["INSTITUTION"]) in control_no: + author['u0'] = control_no # overwrite if multiples + elif pattern % (CFG_BIBAUTHORITY_TYPE_NAMES["AUTHOR"]) in control_no: + author['a0'] = control_no # overwrite if multiples + + if relator_code_pattern: p = re.compile(relator_code_pattern) authors = filter(lambda x: p.match(x.get('4', '')), authors) @@ -91,13 +111,31 @@ def format_element(bfo, limit, separator=' ; ', '&f=author&p=' + quote(author['a']) + \ '">' + escape(author['a']) + '' else: + auth_coll_param = '' + if 'a0' in author: + recIDs = get_low_level_recIDs_from_control_no(author['a0']) + if len(recIDs): + auth_coll_param = '&c=' + \ + CFG_BIBAUTHORITY_AUTHORITY_COLLECTION_NAME author['a'] = '' + escape(author['a']) + '' if author.has_key('u'): if print_affiliations == "yes": + if 'u0' in author: + recIDs = get_low_level_recIDs_from_control_no(author['u0']) + # if there is more than 1 recID, clicking on link and + # thus displaying the authority record's page should + # contain a warning that there are multiple authority + # records with the same control number + if len(recIDs): + author['u'] = '' + author['u'] + '' author['u'] = affiliation_prefix + author['u'] + \ affiliation_suffix diff --git a/modules/bibformat/lib/elements/bfe_publisher.py b/modules/bibformat/lib/elements/bfe_publisher.py index 3a5f78261f..e0a50f7128 100644 --- a/modules/bibformat/lib/elements/bfe_publisher.py +++ b/modules/bibformat/lib/elements/bfe_publisher.py @@ -20,6 +20,10 @@ """ __revision__ = "$Id$" +from invenio.config import CFG_SITE_URL + +from invenio.bibauthority_engine import get_low_level_recIDs_from_control_no + def format_element(bfo): """ Prints the publisher name @@ -28,6 +32,23 @@ def format_element(bfo): """ publisher = bfo.field('260__b') + control_no = bfo.field('260__0') if publisher != "sine nomine": + if control_no: + recIDs = get_low_level_recIDs_from_control_no(control_no) + if len(recIDs): + publisher = '' + publisher + '' return publisher + + +def escape_values(bfo): + """ + Called by BibFormat in order to check if output of this element + should be escaped. + """ + return 0 + diff --git a/modules/bibindex/lib/bibindex_engine.py b/modules/bibindex/lib/bibindex_engine.py index 2d97cadc8c..cf1fd6b207 100644 --- a/modules/bibindex/lib/bibindex_engine.py +++ b/modules/bibindex/lib/bibindex_engine.py @@ -26,6 +26,7 @@ import re import sys import time +import fnmatch from invenio.config import CFG_SOLR_URL @@ -33,14 +34,23 @@ CFG_MYSQL_THREAD_TIMEOUT, \ CFG_CHECK_MYSQL_THREADS, \ CFG_BIBINDEX_COLUMN_VALUE_SEPARATOR, \ - CFG_BIBINDEX_INDEX_TABLE_TYPE + CFG_BIBINDEX_INDEX_TABLE_TYPE, \ + CFG_BIBINDEX_ADDING_RECORDS_STARTED_STR +from invenio.bibauthority_config import \ + CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC, \ + CFG_BIBAUTHORITY_RECORD_CONTROL_NUMBER_FIELD, \ + CFG_BIBAUTHORITY_BIBINDEX_UPDATE_MESSAGE +from invenio.bibauthority_engine import get_index_strings_by_control_no,\ + get_control_nos_from_recID from invenio.bibindexadminlib import get_idx_remove_html_markup, \ get_idx_remove_latex_markup, \ get_idx_remove_stopwords from invenio.bibdocfile import BibRecDocs from invenio.search_engine import perform_request_search, \ get_index_stemming_language, \ - get_synonym_terms + get_synonym_terms, \ + search_pattern, \ + search_unit_in_bibrec from invenio.dbquery import run_sql, DatabaseError, serialize_via_marshal, \ deserialize_via_marshal, wash_table_column_name from invenio.bibindex_engine_washer import wash_index_term @@ -57,6 +67,7 @@ CFG_JOURNAL_PUBINFO_STANDARD_FORM_REGEXP_CHECK from invenio.bibindex_engine_utils import get_all_index_names_and_column_values, \ load_tokenizers +from invenio.search_engine_utils import get_fieldvalues @@ -89,6 +100,13 @@ def list_union(list1, list2): union_dict[e] = 1 return union_dict.keys() +def list_unique(_list): + """Returns a _list with duplicates removed.""" + _dict = {} + for e in _list: + _dict[e] = 1 + return _dict.keys() + ## safety function for killing slow DB threads: def kill_sleepy_mysql_threads(max_threads=CFG_MAX_MYSQL_THREADS, thread_timeout=CFG_MYSQL_THREAD_TIMEOUT): """Check the number of DB threads and if there are more than @@ -727,7 +745,7 @@ def add_recIDs(self, recIDs, opt_flush): solr_commit() raise - write_message("%s adding records #%d-#%d started" % \ + write_message(CFG_BIBINDEX_ADDING_RECORDS_STARTED_STR % \ (self.tablename, i_low, i_high)) if CFG_CHECK_MYSQL_THREADS: kill_sleepy_mysql_threads() @@ -738,7 +756,7 @@ def add_recIDs(self, recIDs, opt_flush): flush_count = flush_count + i_high - i_low + 1 chunksize_count = chunksize_count + i_high - i_low + 1 records_done = records_done + just_processed - write_message("%s adding records #%d-#%d ended " % \ + write_message(CFG_BIBINDEX_ADDING_RECORDS_STARTED_STR % \ (self.tablename, i_low, i_high)) if chunksize_count >= chunksize: @@ -810,6 +828,37 @@ def add_recIDs_by_date(self, dates, opt_flush): write_message("No new records added by author canonical IDs. %s is up to date" % self.tablename) else: self.add_recIDs(alist, opt_flush) + # special case of authority controlled indexes where we need to re-index + # those records that were affected by changed Authority Records: + res = intbitset() + for tag in self.fields_to_index: + pattern = tag.replace('%', '*') + matches = fnmatch.filter(CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC.keys(), pattern) + if not len(matches): + continue + for tag_match in matches: + # get the type of authority record associated with this field + auth_type = CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC.get(tag_match) + # find updated authority records of this type + # dates[1] is ignored, needs dates[0] to find res + auth_recIDs = search_pattern(p='980__a:' + auth_type) \ + & search_unit_in_bibrec(str(dates[0]), 'now()', type='m') + # now find dependent bibliographic records + for auth_recID in auth_recIDs: + # get the fix authority identifier of this authority record + control_nos = get_control_nos_from_recID(auth_recID) + # there may be multiple control number entries! (the '035' field is repeatable!) + for control_no in control_nos: + # get the bibrec IDs that refer to AUTHORITY_ID in TAG + tag_0 = tag_match[:5] + '0' # possibly do the same for '4' subfields ? + fieldvalue = '"' + control_no + '"' + res |= search_pattern(p=tag_0 + ':' + fieldvalue) + authority_list = create_range_list(list(res)) + if not authority_list: + write_message("No new authority records added. %s is up to date" % self.tablename) + else: + write_message(CFG_BIBAUTHORITY_BIBINDEX_UPDATE_MESSAGE) + self.add_recIDs(authority_list, opt_flush) def add_recID_range(self, recID1, recID2): """Add records from RECID1 to RECID2.""" @@ -856,6 +905,21 @@ def add_recID_range(self, recID1, recID2): new_words = get_words_function(phrase) wlist[recID] = list_union(new_words, wlist[recID]) + #authority records + pattern = tag.replace('%', '*') + matches = fnmatch.filter(CFG_BIBAUTHORITY_CONTROLLED_FIELDS_BIBLIOGRAPHIC.keys(), pattern) + if not len(matches): + continue + for tag_match in matches: + authority_tag = tag_match[0:3] + "__0" + for recID in xrange(int(recID1), int(recID2) + 1): + control_nos = get_fieldvalues(recID, authority_tag) + for control_no in control_nos: + new_strings = get_index_strings_by_control_no(control_no) + for string_value in new_strings: + new_words = get_words_function(string_value) + wlist[recID] = list_union(new_words, wlist[recID]) + # lookup index-time synonyms: synonym_kbrs = get_all_synonym_knowledge_bases() if synonym_kbrs.has_key(self.index_name): diff --git a/modules/bibindex/lib/bibindex_engine_config.py b/modules/bibindex/lib/bibindex_engine_config.py index 3bc550ccfc..fffa700c60 100644 --- a/modules/bibindex/lib/bibindex_engine_config.py +++ b/modules/bibindex/lib/bibindex_engine_config.py @@ -50,5 +50,7 @@ CFG_BIBINDEX_TOKENIZERS_PATH = os.path.join(CFG_PYLIBDIR, 'invenio', 'bibindex_tokenizers') +CFG_BIBINDEX_ADDING_RECORDS_STARTED_STR = "%s adding records #%d-#%d started" + diff --git a/modules/bibindex/lib/bibindex_engine_unit_tests.py b/modules/bibindex/lib/bibindex_engine_unit_tests.py index edbd725c00..18bcb083b9 100644 --- a/modules/bibindex/lib/bibindex_engine_unit_tests.py +++ b/modules/bibindex/lib/bibindex_engine_unit_tests.py @@ -42,6 +42,12 @@ def test_list_union(self): bibindex_engine.list_union([1, 2, 3], [1, 3, 4])) + def test_list_unique(self): + """bibindex engine - list unique""" + self.assertEqual([1, 2, 3], + bibindex_engine.list_unique([1, 2, 3, 3, 1, 2])) + + class TestWashIndexTerm(unittest.TestCase): """Tests for washing index terms, useful for both searching and indexing.""" diff --git a/modules/bibindex/lib/bibindex_regression_tests.py b/modules/bibindex/lib/bibindex_regression_tests.py index 0ef619063b..132cb462e6 100644 --- a/modules/bibindex/lib/bibindex_regression_tests.py +++ b/modules/bibindex/lib/bibindex_regression_tests.py @@ -22,12 +22,28 @@ __revision__ = "$Id$" import unittest +import os +from invenio.bibauthority_config import CFG_BIBAUTHORITY_BIBINDEX_UPDATE_MESSAGE +from invenio.bibindex_engine_config import CFG_BIBINDEX_ADDING_RECORDS_STARTED_STR, \ + CFG_BIBINDEX_INDEX_TABLE_TYPE +from invenio.bibtask import task_low_level_submission +from invenio.config import CFG_BINDIR, CFG_LOGDIR from invenio.testutils import make_test_suite, run_test_suite, nottest from invenio.dbquery import run_sql, deserialize_via_marshal from invenio.bibindex_engine import WordTable, get_index_id_from_index_name, get_index_tags from invenio.intbitset import intbitset -from invenio.bibindex_engine_config import CFG_BIBINDEX_INDEX_TABLE_TYPE +from invenio.search_engine_utils import get_fieldvalues +from invenio.bibauthority_engine import get_index_strings_by_control_no, get_control_nos_from_recID + + +def reindex_for_type_with_bibsched(_type): + """runs bibindex for the index '_type' and returns the task_id""" + program = os.path.join(CFG_BINDIR, 'bibindex') + task_id = task_low_level_submission('bibindex', 'bibindex_regression_tests', '-w', _type, '-u', 'admin') + COMMAND = "%s %s > /dev/null 2> /dev/null" % (program, str(task_id)) + os.system(COMMAND) + return task_id def prepare_for_index_update(index_id, remove_stopwords = '', @@ -571,6 +587,65 @@ def test_splliting_and_indexing_CJK_characters_reversed_table(self): self.assertEqual(iset, ['\xe6\x95\xac', '\xe7\x8d\xa8', '\xe4\xba\xad', '\xe5\x9d\x90']) +class BibIndexAuthorityRecordTest(unittest.TestCase): + """Test if BibIndex correctly knows when to update the index for a + bibliographic record if it is dependent upon an authority record changed + within the given date range""" + + def test_authority_record_recently_updated(self): + """bibindex - reindexing after recently changed authority record""" + + authRecID = 118 + bibRecID = 9 + index_name = 'author' + table = "idxWORD%02dF" % get_index_id_from_index_name(index_name) + reindex_for_type_with_bibsched(index_name) + run_sql("UPDATE bibrec SET modification_date = now() WHERE id = %s", (authRecID,)) + # run bibindex again + task_id = reindex_for_type_with_bibsched(index_name) + + filename = os.path.join(CFG_LOGDIR, 'bibsched_task_' + str(task_id) + '.log') + _file = open(filename) + text = _file.read() # small file + _file.close() + self.assertTrue(text.find(CFG_BIBAUTHORITY_BIBINDEX_UPDATE_MESSAGE) >= 0) + self.assertTrue(text.find(CFG_BIBINDEX_ADDING_RECORDS_STARTED_STR % (table, bibRecID, bibRecID)) >= 0) + + def test_authority_record_enriched_index(self): + """bibindex - test whether reverse index for bibliographic record + contains words from referenced authority records""" + bibRecID = 9 + authority_string = 'jonathan' + index_name = 'author' + table = "idxWORD%02dR" % get_index_id_from_index_name(index_name) + + reindex_for_type_with_bibsched(index_name) + self.assertTrue( + authority_string in deserialize_via_marshal( + run_sql("SELECT termlist FROM %s WHERE id_bibrec = %s" % (table, bibRecID))[0][0] + ) + ) + + def test_indexing_of_deleted_authority_record(self): + """bibindex - no info for indexing from deleted authority record""" + recID = 119 # deleted record + control_nos = get_control_nos_from_recID(recID) + info = get_index_strings_by_control_no(control_nos[0]) + self.assertEqual([], info) + + def test_authority_record_get_values_by_bibrecID_from_tag(self): + """bibindex - find authors in authority records for given bibrecID""" + tags = ['100__a'] + bibRecID = 9 + values = [] + for tag in tags: + authority_tag = tag[0:3] + "__0" + control_nos = get_fieldvalues(bibRecID, authority_tag) + for control_no in control_nos: + new_strings = get_index_strings_by_control_no(control_no) + values.extend(new_strings) + self.assertTrue('Ellis, Jonathan Richard' in values) + TEST_SUITE = make_test_suite(BibIndexRemoveStopwordsTest, BibIndexRemoveLatexTest, @@ -578,7 +653,8 @@ def test_splliting_and_indexing_CJK_characters_reversed_table(self): BibIndexYearIndexTest, BibIndexAuthorCountIndexTest, BibIndexJournalIndexTest, - BibIndexCJKTokenizerTitleIndexTest) + BibIndexCJKTokenizerTitleIndexTest, + BibIndexAuthorityRecordTest) if __name__ == "__main__": run_test_suite(TEST_SUITE, warn_user=True) diff --git a/modules/bibknowledge/lib/bibknowledge_regression_tests.py b/modules/bibknowledge/lib/bibknowledge_regression_tests.py index 77d035c52d..b72b54fa1a 100644 --- a/modules/bibknowledge/lib/bibknowledge_regression_tests.py +++ b/modules/bibknowledge/lib/bibknowledge_regression_tests.py @@ -204,7 +204,7 @@ def test_kbd_search_as_json(self): def test_kb_for_bibedit(self): """bibknowledge - test a bibedit-style *very* dynamic kb""" myvalues = get_kbd_values_for_bibedit("100__a", searchwith="Ellis", expression="100__a:*%*") - self.assertEqual(1, len(myvalues)) + self.assertEqual(2, len(myvalues)) def test_kb_attribute_update(self): """bibknowledge - attribute modifications persist in database""" diff --git a/modules/bibrank/lib/bibrank_regression_tests.py b/modules/bibrank/lib/bibrank_regression_tests.py index 7a537fce51..94b2947824 100644 --- a/modules/bibrank/lib/bibrank_regression_tests.py +++ b/modules/bibrank/lib/bibrank_regression_tests.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ## ## This file is part of Invenio. -## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 CERN. +## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 CERN. ## ## Invenio is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as @@ -79,13 +79,13 @@ def test_search_results_ranked_by_similarity(self): """bibrank - search results ranked by word similarity""" self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=ellis&rm=wrd&of=id', - expected_text="[8, 10, 17, 11, 12, 13, 47, 16, 9, 14, 18, 15]")) + expected_text="[8, 10, 11, 12, 47, 17, 13, 16, 9, 14, 18, 118, 15]")) def test_similar_records_link(self): """bibrank - 'Similar records' link""" self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=recid%3A77&rm=wrd&of=id', - expected_text="[84, 96, 95, 85, 77]")) + expected_text="[96, 95, 85, 77]")) class BibRankCitationRankingTest(unittest.TestCase): """Check BibRank citation ranking tools.""" diff --git a/modules/bibrecord/lib/bibrecord_unit_tests.py b/modules/bibrecord/lib/bibrecord_unit_tests.py index 7ff7404a7a..0845c5f99e 100644 --- a/modules/bibrecord/lib/bibrecord_unit_tests.py +++ b/modules/bibrecord/lib/bibrecord_unit_tests.py @@ -65,19 +65,20 @@ def setUp(self): def test_records_created(self): """ bibrecord - demo file how many records are created """ - self.assertEqual(113, len(self.recs)) + self.assertEqual(141, len(self.recs)) def test_tags_created(self): """ bibrecord - demo file which tags are created """ ## check if the tags are correct tags = ['003', '005', '020', '024', '035', '037', '041', '080', '084', '088', - '100', '110', '242', '245', '246', '250', '260', '269', '270', - '300', '340', '490', '500', '502', '506', '520', '542', - '590', '595', '650', '653', '690', '691', '693', '694', '695', '697', - '700', '710', '711', '720', '773', '852', '856', '859', '901', '909', - '916', '960', '961', '962', '963', '964', '970', '980', - '999', 'FFT'] + '100', '110', '148', '150', '242', '245', '246', '250', '260', '269', + '270', '300', '340', '371', '372', '400', '410', '430', '440', '450', + '490', '500', '502', '506', '510', '520', '542', '550', '588', '590', + '595', '643', '650', '653', '670', '678', '680', '690', '691', '693', + '694', '695', '697', '700', '710', '711', '720', '773', '852', '856', + '859', '901', '909', '913', '914', '916', '920', '960', '961', '962', + '963', '964', '970', '980', '999', 'FFT'] t = [] for rec in self.recs: @@ -88,7 +89,6 @@ def test_tags_created(self): for x in t: if not x in tt: tt.append(x) - self.assertEqual(tags, tt) def test_fields_created(self): @@ -103,8 +103,8 @@ def test_fields_created(self): 11, 15, 8, 11, 14, 13, 12, 13, 6, 6, 25, 24, 27, 26, 26, 24, 26, 26, 25, 28, 24, 23, 27, 25, 25, 26, 26, 25, 20, 26, 25, 22, 9, 8, 9, 9, 8, 7, 19, 21, 27, 23, - 23, 22, 9, 8, 16] - + 23, 22, 9, 8, 16, 7, 7, 9, 5, 5, 3, 9, 12, 6, + 8, 8, 8, 13, 20, 20, 5, 8, 7, 7, 7, 7, 7, 8, 7, 8, 7, 7, 8] cr = [] ret = [] for rec in self.recs: diff --git a/modules/bibsort/bin/bibsort.in b/modules/bibsort/bin/bibsort.in old mode 100755 new mode 100644 diff --git a/modules/miscutil/demo/demobibdata.xml b/modules/miscutil/demo/demobibdata.xml index d2998f51c5..8282233398 100644 --- a/modules/miscutil/demo/demobibdata.xml +++ b/modules/miscutil/demo/demobibdata.xml @@ -1,3 +1,19 @@ + + + + + + + @@ -824,6 +840,7 @@
Ellis, J + AUTHOR|(SzGeCERN)aaa0005 University of Oxford @@ -2422,6 +2439,7 @@ Mangano, M L CERN + INSTITUTION|(SzGeCERN)iii0002 Physics at the front-end of a neutrino factory : a quantitative appraisal @@ -2430,6 +2448,7 @@ Geneva CERN 16 May 2001 + INSTITUTION|(SzGeCERN)iii0002 1 p @@ -2467,6 +2486,7 @@ Ellis, J + AUTHOR|(SzGeCERN)aaa0005 Forte, S @@ -2518,6 +2538,7 @@ CERN + INSTITUTION|(SzGeCERN)iii0002 nuDIS Working group of the ECFA-CERN Neutrino-Factory Study Group @@ -5173,7 +5194,9 @@ Ellis, J + AUTHOR|(SzGeCERN)aaa0005 CERN + INSTITUTION|(SzGeCERN)iii0002 From the standard model to grand unification @@ -6010,6 +6033,7 @@ Albajar, C CERN + INSTITUTION|(SzGeCERN)iii0002 Multifractal analysis of minimum bias events in \Sqrt s = 630 GeV $\overline{p}$p collisions @@ -6352,7 +6376,9 @@ Ellis, J + AUTHOR|(SzGeCERN)aaa0005 CERN + INSTITUTION|(SzGeCERN)iii0002 Non-compact supergravity solves problems @@ -7918,6 +7944,7 @@ Bento, M C CERN + INSTITUTION|(SzGeCERN)iii0002 Supergravity Inflation on the Brane @@ -11608,6 +11635,7 @@ Aglietti, U CERN + INSTITUTION|(SzGeCERN)iii0002 A new model-independent way of extracting |V_ub/V_cb| @@ -24575,4 +24603,1683 @@ Only the mountain and I. SMALLTHUMB + + + + + + + + + DLC + 19951121053638.0 + + (OCoLC)oca00230701 + + + AUTHOR|(SzGeCERN)aaa0001 + + + Mann, Thomas + 1875-1955 + + + Man, Tomas + 1875-1955 + + + Mann, Tomas + 1875-1955 + + + Mān, Tūmās + 1875-1955 + + + Mann, Paul Thomas + 1875-1955 + + + Thomas, Paul + 1875-1955 + + + Mani, Tʿomas + 1875-1955 + + + Man, Tʿomasŭ + 1875-1955 + + + Mann, Tomasz + 1875-1955 + + + His Königliche Hoheit, 1909. + + + Volgina, A.A. Tomas Mann--biobibliogr. ukazatelʹ, 1979: + t.p. (Tomas Mann) + + + Najīb, N. Qiṣṣat al-ajyāl bayna Tūmās Mān wa-Najīb Maħfūẓ, 1982: + t.p. (Tūmās Mān) + + + Vaget, H.R. Thomas Mann-Kommentar zu sämtlichen Erzählungen, c1984: + t.p. (Thomas Mann) p. 13, etc. (b. 6-6-1875 in Lübeck as Paul Thomas Mann; used pseud. Paul Thomas as co-editor of student newspaper in 1893; d. 8-12-1955) + + + Kakabadze, N. Tomas Mann, 1985: + t.p. (Tomas Mann) added t.p. (Tʿomas Mani) + + + Chʿoe, S.B. Tʿomasŭ Man yŏnʾgu, 1981: + t.p. (Tʿomasŭ Man) + + + Łukosz, J. Terapia jako duchowa forma życia, 1990: + t.p. (Tomasza Manna) + + + AUTHORITY + + + AUTHOR + + + + + DLC + 19991204070327.0 + + AUTHOR|(OCoLC)oca00955355 + + + AUTHOR|(SzGeCERN)aaa0002 + + + Bach, Johann Sebastian + 1685-1750. + Es erhub sich ein Streit + + + Bach, Johann Sebastian + 1685-1750. + See how fiercely they fight + + + Bach, Johann Sebastian + 1685-1750. + There uprose a great strife + + + Bach, Johann Sebastian + 1685-1750. + Cantatas, + BWV 19 + + + Bach, Johann Sebastian + 1685-1750. + Cantatas, + no. 19 + + + Bach, Johann Sebastian + 1685-1750. + There arose a great strife + + + Bach, Johann Sebastian + 1685-1750. + Kantate am Michaelisfest + BWV 19 + + + Bach, Johann Sebastian + 1685-1750. + Festo Michaelis + BWV 19 + + + Bach, Johann Sebastian + 1685-1750. + Kantate zum Michaelistag + BWV 19 + + + Bach, Johann Sebastian + 1685-1750. + Cantata for Michaelmas Day + BWV 19 + + + Bach, Johann Sebastian + 1685-1750. + Cantate am Michaelisfeste + BWV 19 + + + Bach, J.S. BWV 19, Es erhub sich ein Streit [SR] p1988: + label (BWV 19, Es erhub sich ein Streit = There arose a great strife) + + + Schmieder, 1990 + (19. Es erhub sich ein Streit; Kantate am Michaelisfest (Festo Michaelis)) + + + AUTHORITY + + + AUTHOR + + + + + DLC + 19850502074119.0 + + AUTHOR|(SzGeCERN)aaa0003 + + + Bach, Johann Sebastian + 1685-1750. + Keyboard music. + Selections (Bach Guild) + + + Bach, Johann Sebastian + 1685-1750. + Historical anthology of music. + V, + Baroque (late). + F, + Johann Sebastian Bach. + 1, + Works for keyboard + + + Historical anthology of music. + V, + Baroque (late). + F, + Johann Sebastian Bach. + 1, + Works for keyboard + + + nnaa + Historical anthology of music + period V, + category F, + sub-category 1 + + + Johann Sebastian Bach. + 1 + Works for keyboard + + + Bach, Johann Sebastian + 1685-1750. + Johann Sebastian Bach. + 1, + Works for keyboard + + + Bach, Johann Sebastian + 1685-1750. + Works for keyboard + + + New York, NY + Bach Guild + + + Bach, J.S. Organ works [SR] c1981. + + + AUTHORITY + + + AUTHOR + + + + 19960528091722.0 + + Solar energy technology handbook, c1980- (a.e.) + v. 1, t.p. (William C. Dickinson) pub. info sheet (William Clarence Dickinson, b. 3/15/22) + + + Dickinson, William C. + 1922- + + + AUTHOR|(DLC)n 80007472 + + + AUTHOR|(SzGeCERN)aaa0004 + + + AUTHORITY + + + AUTHOR + + + + + + Europhysics Study Conference on Unification of the Fundamental Particle Interactions, Erice, Italy, 1980. Unification of the fundamental particle interactions, 1980 (a.e.) + t.p. (John Ellis) + + + Supersymmetry and supergravity, c1986: + CIP t.p. (J. Ellis) + + + Quantum reflections, 2000: + CIP t.p. (John Ellis) data sht. (b. July 1, 1946) pub. info. (Jonathan Richard Ellis) + + + Ellis, J. + 1946- + (John), + + + Ellis, Jonathan Richard + 1946- + + + Ellis, John + 1946- + + + AUTHOR|(DLC)n 80141717 + + + AUTHOR|(SzGeCERN)aaa0005 + + + AUTHORITY + + + AUTHOR + + + + + + EllisDeleted, JohnDeleted + 1946- + + + AUTHOR|(SzGeCERN)aaa0006 + + + AUTHORITY + + + AUTHOR + + + DELETED + + + + + + Stanford Linear Accelerator Center + SLAC + + + SLAC STANFORD + DESY_AFF + + + DOE + HEP200 + LABLIST + PDGLIST + PPF + SLUO + TOP050 + TOP100 + TOP200 + TOP500 + WEB + + + http://www.slac.stanford.edu/ + + + 2011-01-21 + + + 1989-07-18 + + + INST-6300 + + + Accel. Ctr. Stanford Linear Center + + + SLAC Stanford + DESY + + + CORE + + + INSTITUTION|(SzGeCERN)iii0001 + + + 94025 + US + SLAC National Accelerator Laboratory + SLAC National Accelerator Laboratory + 2575 Sand Hill Road + 2575 Sand Hill Road + Menlo Park, CA 94025-7090 + Menlo Park, CA 94025-7090 + USA + USA + CA + Menlo Park + + + AUTHORITY + + + INSTITUTION + + + + + http://www.cern.ch + + + 2011-01-21 + + + 1989-07-16 + + + INST-1147 + + + Conseil européen pour la Recherche Nucléaire (1952-1954) + Organisation européenne pour la Recherche nucléaire (1954-now) + Sub title: Laboratoire européen pour la Physique des Particules (1984-now) + Sub title: European Laboratory for Particle Physics (1984-now) + + + HEP200 + LABLIST + PDGLIST + PPF + SLUO + TOP050 + TOP100 + TOP200 + TOP500 + WEB + + + DESY + CERN Geneva + + + Centre Européen de Recherches Nucléaires + center + + + European Organization for Nuclear Research + + + CERN + CERN + CERN + + + CH-1211 Genève 23 + Geneva + Switzerland + 1211 + CH + + + Research centre + + + 2nd address: Organisation Européenne pour la Recherche Nucléaire (CERN), F-01631 Prévessin Cedex, France + + + CORE + + + INSTITUTION|(SzGeCERN)iii0002 + + + AUTHORITY + + + INSTITUTION + + + + + 19891121083347.0 + + SUBJECT|(DLC)sh 85101653 + + + SUBJECT|(SzGeCERN)sss0001 + + + Physics + + + Natural philosophy + + + Philosophy, Natural + + + g + Physical sciences + + + Dynamics + + + AUTHORITY + + + SUBJECT + + + + DLC + 20010904160459.0 + + SUBJECT|(SzGeCERN)sss0003 + + + Computer crimes + + + Computer fraud + + + Computers + Law and legislation + Criminal provisions + + + Computers and crime + + + Cyber crimes + + + Cybercrimes + + + Electronic crimes (Computer crimes) + + + g + Crime + + + Privacy, Right of + + + 00351496: Adamski, A. Prawo kame komputerowe, c2000. + + + Excite WWW directory of subjects, July 10, 2001 + (cybercrimes; subcategory under Criminal, Branches of law, Law, Education) + + + Electronic crime needs assessment for state and local law enforcement, 2001: + glossary, p. 41 (electronic crime includes but is not limited to fraud, theft, forgery, child pornography or exploitation, stalking, traditional white-collar crimes, privacy violations, illegal drug transactions, espionage, computer intrusions; no synonyms given) + + + AUTHORITY + + + SUBJECT + + + + DLC + 20010904162409.0 + + SUBJECT|(SzGeCERN)sss0004 + + + Embellishment (Music) + + + Diminution (Music) + + + Ornamentation (Music) + + + Ornaments (Music) + + + g + Music + Performance + + + g + Performance practice (Music) + + + Musical notation + + + Variation (Music) + + + Heim, N.M. Ornamentation for the clarinetist, c1993. + + + Massin. De la variation, c2000. + + + AUTHORITY + + + SUBJECT + + + + DLC + 20010904162503.0 + + SUBJECT|(SzGeCERN)sss0005 + + + Embellishment (Vocal music) + + + Colorature + + + Fioriture + + + g + Embellishment (Music) + + + g + Vocal music + History and criticism + + + Massin. De la variation, c2000. + + + AUTHORITY + + + SUBJECT + + + + + + + + + + + PER:749 + Kilian, K. + 0 + fzj + + + Other ways to make polarized antiproton beams + + + 2010 + + + 107 + + + 6697 + Verhandlungen der Deutschen Physikalischen Gesellschaft (Reihe 06) + 2 + + + Journal Article + + + PER:513 + Grzonka, D. + 1 + fzj + + + PER:1182 + Oelert, W. + 2 + fzj + + + --NOT MATCHED-- + Vol. 2 + 2:< + 2 + 2010 + + + GRANT:413 + P53 + Struktur der Materie der Materie + Physik der Hadronen und Kerne + + + 2010 + + + INSTITUTION|(DE-Juel1)795 + IKP + IKP-1 + Experimentelle Hadronstruktur + + + VDB:126525 + + + VDB + + + + + + DOI + 10.1063/1.2737136 + + + VDB + VDB:88636 + + + WOS + WOS:000246413400056 + + + ISSN + 0003-6951 + + + inh:9498831 + inh + + + English + + + Inspec + Atom-, molecule-, and ion-surface impact and interactions + + + Inspec + Pulsed laser deposition + + + Inspec + Stoichiometry and homogeneity + + + Inspec + Thin film growth, structure, and epitaxy + + + Inspec + Vacuum deposition + + + 39744 + Heeg, T. + 0 + fzj + + + Epitaxially stabilized growth of orthorhombic LuScO3 thin films + + + 2007 + + + 192901-1 - 192901-3 + + + 562 + Applied Physics Letters + 90 + 0003-6951 + + + Journal Article + + + Metastable lutetium scandate (LuScO3) thin films with an orthorhombic perovskite structure have been prepared by molecular-beam epitaxy and pulsed-laser deposition on NdGaO3(110) and DyScO3(110) substrates. Stoichiometry and crystallinity were investigated using Rutherford backscattering spectrometry/channeling, x-ray diffraction, and transmission electron microscopy. The results indicate that LuScO3, which normally only exists as a solid solution of Sc2O3 and Lu2O3 with the cubic bixbyite structure, can be grown in the orthorhombically distorted perovskite structure. Rocking curves as narrow as 0.05deg were achieved. A critical film thickness of approximately 200 nm for the epitaxially stabilized perovskite polymorph of LuScO3 on NdGaO3(110) substrates was determined. + + + Enriched from Web of Science, Inspec + + + Inspec + DyScO3 + + + Inspec + DyScO3(110) substrates + + + Inspec + Fuel cells + + + Inspec + LuScO3 + + + Inspec + NdGaO3 + + + Inspec + NdGaO3(110) substrates + + + Inspec + Rutherford backscattering + + + Inspec + Rutherford backscattering channeling + + + Inspec + Rutherford backscattering spectrometry + + + Inspec + X-ray diffraction + + + Inspec + critical film thickness + + + Inspec + crystallinity + + + Inspec + epitaxial layers + + + Inspec + epitaxially stabilized growth + + + Inspec + epitaxially stabilized perovskite polymorph + + + Inspec + lutetium compounds + + + Inspec + metastable lutetium scandate thin films + + + Inspec + molecular beam epitaxial growth + + + Inspec + molecular beam epitaxy + + + Inspec + orthorhombically distorted perovskite structure + + + Inspec + polymorphism + + + Inspec + pulsed laser deposition + + + Inspec + rocking curves + + + Inspec + stoichiometry + + + Inspec + transmission electron microscopy + + + PER:64142 + Roeckerath, M. + 1 + fzj + + + PER:5409 + Schubert, J. + 2 + fzj + + + PER:5482 + Zander, W. + 3 + fzj + + + PER:14557 + Buchal, Ch. + 4 + fzj + + + PER:60616 + Chen, H. Y. + 5 + fzj + + + PER:5020 + Jia, C. L. + 6 + fzj + + + PER:45799 + Jia, Y. + 7 + fzj + + + PER:65921 + Adamo, C. + 8 + + + PER:15220 + Schlom, D. G. + 9 + + + ZDBID:2265524-4 + 10.1063/1.2737136 + Vol. 90, no. 19, p. 192901 + 19 + 90:19<192901 + Applied physics reviews + 90 + 0003-6951 + 2007 + + + http://dx.doi.org/10.1063/1.2737136 + + + GRANT:412 + P42 + Schlüsseltechnologien + Grundlagen für zukünftige Informationstechnologien + + + 2007 + + + INSTITUTION|(DE-Juel1)381 + 14.09.2008 + CNI + CNI + Center of Nanoelectronic Systems for Information Technology + Zusammenschluss der am FE-Vorhaben I01 beteiligtenute: IFF-TH-I, IFF-TH-II, IFF-IEM, IFF-IMF, IFF-IEE, ISG-1, ISG-2, ISG-3 + + + INSTITUTION|(DE-Juel1)788 + IFF + IFF-8 + Mikrostrukturforschung + + + INSTITUTION|(DE-Juel1)799 + IBN + IBN-1 + Halbleiter-Nanoelektronik + + + VDB:88636 + + + VDB + + + ARTICLE + + + + + + DOI + 10.4028/www.scientific.net/MSF.638-642.1098 + + + VDB + VDB:125298 + + + ISSN + 0255-5476 + + + inh:11707323 + inh + + + English + + + Inspec + Fuel cells + + + PER:96536 + Menzler, N.H. + 0 + fzj + + + Influence of processing parameters on the manufacturing of anode-supported solid oxide fuel cells by different wet chemical routes + + + 2010 + + + + + + 4206 + Materials Science Forum + 638-642 + 0255-5476 + 1098 - 1105 + + + Journal Article + + + Anode-supported solid oxide fuel cells (SOFC) are manufactured at Forschungszentrum Jülich by different wet chemical powder processes and subsequent sintering at high temperatures. Recently, the warm pressing of Coat-Mix powders has been replaced by tape casting as the shaping technology for the NiO/8YSZ-containing substrate in order to decrease the demand for raw materials due to lower substrate thickness and in order to increase reproducibility and fabrication capacities (scalable process). Different processing routes for the substrates require the adjustment of process parameters for further coating with functional layers. Therefore, mainly thermal treatment steps have to be adapted to the properties of the new substrate types in order to obtain high-performance cells with minimum curvature (for stack assembly). In this presentation, the influence of selected process parameters during cell manufacturing will be characterized with respect to the resulting physical parameters such as slurry viscosity, green tape thickness, relative density, substrate strength, electrical conductivity, and shrinkage of the different newly developed substrate types. The influencing factors during manufacturing and the resulting characteristics will be presented and possible applications for the various substrates identified. + + + Enriched from Inspec + + + Inspec + anode-supported solid oxide fuel cells + + + Inspec + coating + + + Inspec + electrical conductivity + + + Inspec + green tape thickness + + + Inspec + powders + + + Inspec + processing parameters + + + Inspec + shrinkage + + + Inspec + sintering + + + Inspec + slurry viscosity + + + Inspec + solid oxide fuel cells + + + Inspec + tape casting + + + Inspec + thermal treatment + + + Inspec + viscosity + + + Inspec + warm pressing + + + Inspec + wet chemical powder processes + + + PER:76694 + Schafbauer, W. + 1 + fzj + + + PER:96316 + Buchkremer, H.P. + 2 + fzj + + + ZDBID:2047372-2 + 10.4028/www.scientific.net/MSF.638-642.1098 + Vol. 638-642, p. 1098 - 1105 + 638-642:<1098 - 1105 + Materials science forum + 638-642 + 0255-5476 + 2010 + + + http://dx.doi.org/10.4028/www.scientific.net/MSF.638-642.1098 + + + GRANT:402 + P12 + Energie + Rationelle Energieumwandlung + + + 2010 + + + INSTITUTION|(DE-Juel1)1130 + IEK + IEK-1 + Werkstoffsynthese und Herstellverfahren + + + VDB:125298 + + + VDB + + + ARTICLE + + + + + + + + INSTITUTION|(DE-Juel1)5008462-8 + + + Forschungszentrum Jülich + + + Energieforschungszentrum Jülich + + + KFA + + + Research Centre Jülich + + + KFA Jülich + + + FZJ + + + a + Kernforschungsanlage Jülich + + + INSTITUTION + + + AUTHORITY + + + + + + INSTITUTION|(DE-Juel1)301 + INST + + + INSTITUTION|(DE-Juel1)301 + + + Institut für Kernphysik + + + 31.12.2000 + + + IKP + d + + + INSTITUTION|(DE-Juel1)5008462-8 + GND + Forschungszentrum Jülich + t + + + INSTITUTION|(DE-Juel1)301 + INST + IKP + g + + + INSTITUTION|(DE-Juel1)301 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)795 + INST + + + INSTITUTION|(DE-Juel1)795 + + + Experimentelle Hadronstruktur + + + IKP-1 + d + + + INSTITUTION|(DE-Juel1)301 + INST + IKP + g + + + INSTITUTION|(DE-Juel1)221 + INST + Institut 1 (Experimentelle Kernphysik I) + a + + + (DE-Juel1)795 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)241 + INST + + + INSTITUTION|(DE-Juel1)241 + + + Institut für Festkörperforschung + + + IFF + d + + + INSTITUTION|(DE-Juel1)5008462-8 + GND + Forschungszentrum Jülich + t + + + INSTITUTION|(DE-Juel1)241 + INST + IFF + g + + + (DE-Juel1)241 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)1107 + INST + + + INSTITUTION|(DE-Juel1)1107 + + + Institut für Bio- und Nanosysteme + + + IBN + d + + + INSTITUTION|(DE-Juel1)5008462-8 + GND + Forschungszentrum Jülich + t + + + INSTITUTION|(DE-Juel1)1107 + INST + IBN + g + + + (DE-Juel1)1107 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)1125 + INST + + + INSTITUTION|(DE-Juel1)1125 + + + Institut für Energie- und Klimaforschung + + + IEK + d + + + INSTITUTION|(DE-Juel1)5008462-8 + + GND + Forschungszentrum Jülich + t + + + INSTITUTION|(DE-Juel1)1125 + INST + IEK + g + + + (DE-Juel1)1125 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)1115 + INST + + + INSTITUTION|(DE-Juel1)1115 + + + Institut für Energieforschung + + + IEF + d + + + INSTITUTION|(DE-Juel1)5008462-8 + GND + Forschungszentrum Jülich + t + + + INSTITUTION|(DE-Juel1)1115 + INST + IEF + g + + + (DE-Juel1)1115 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)381 + INST + + + INSTITUTION|(DE-Juel1)381 + + + Center of Nanoelectronic Systems for Information Technology + + + 14.09.2008 + + + CNI + d + + + INSTITUTION|(DE-Juel1)5008462-8 + GND + Forschungszentrum Jülich + t + + + INSTITUTION|(DE-Juel1)381 + INST + CNI + g + + + (DE-Juel1)381 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)788 + INST + + + INSTITUTION|(DE-Juel1)788 + + + Mikrostrukturforschung + + + IFF-8 + d + + + INSTITUTION|(DE-Juel1)241 + INST + IFF + g + + + INSTITUTION|(DE-Juel1)37 + INST + Mikrostrukturforschung + a + + + (DE-Juel1)788 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)861 + INST + + + INSTITUTION|(DE-Juel1)861 + + + Institut für Halbleiterschichten und Bauelemente + + + 30.09.2007 + + + IBN-1 + d + + + INSTITUTION|(DE-Juel1)1107 + INST + IBN + g + + + INSTITUTION|(DE-Juel1)41 + INST + Institut für Halbleiterschichten und Bauelemente + a + + + INSTITUTION|(DE-Juel1)799 + INST + Halbleiter-Nanoelektronik + b + + + (DE-Juel1)861 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)799 + INST + + + INSTITUTION|(DE-Juel1)799 + + + Halbleiter-Nanoelektronik + + + IBN-1 + d + + + INSTITUTION|(DE-Juel1)1107 + INST + IBN + g + + + INSTITUTION|(DE-Juel1)861 + INST + Institut für Halbleiterschichten und Bauelemente + a + + + (DE-Juel1)799 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)1130 + INST + + + INSTITUTION|(DE-Juel1)1130 + + + Werkstoffsynthese und Herstellverfahren + + + IEK-1 + d + + + INSTITUTION|(DE-Juel1)1125 + INST + IEK + g + + + INSTITUTION|(DE-Juel1)809 + INST + Werkstoffsynthese und Herstellungsverfahren + a + + + (DE-Juel1)1130 + + + INSTITUTION + + + AUTHORITY + + + + + + (DE-Juel1)809 + INST + + + INSTITUTION|(DE-Juel1)809 + + + Werkstoffsynthese und Herstellungsverfahren + + + 30.09.2010 + + + IEF-1 + d + + + INSTITUTION|(DE-Juel1)1115 + INST + IEF + g + + + INSTITUTION|(DE-Juel1)5 + INST + Werkstoffsynthese und Herstellungsverfahren + a + + + INSTITUTION|(DE-Juel1)1130 + INST + Werkstoffsynthese und Herstellverfahren + b + + + (DE-Juel1)809 + + + INSTITUTION + + + AUTHORITY + + + diff --git a/modules/miscutil/demo/democfgdata.sql b/modules/miscutil/demo/democfgdata.sql index 80ffcf55e5..c8773b0de8 100644 --- a/modules/miscutil/demo/democfgdata.sql +++ b/modules/miscutil/demo/democfgdata.sql @@ -62,6 +62,11 @@ INSERT INTO collection VALUES (30, 'ISOLDE Papers', '980:"ISOLDEPAPER"', NULL, N INSERT INTO collection VALUES (31, 'ISOLDE Internal Notes', '980:"ISOLDENOTE"', NULL, NULL); INSERT INTO collection VALUES (32, 'Drafts', '980:"DRAFT"', NULL, NULL); INSERT INTO collection VALUES (33,'Videos','980:"VIDEO"',NULL,NULL); +INSERT INTO collection VALUES (34, 'Authority Records', 'collection:AUTHORITY', null, null); +INSERT INTO collection VALUES (35, 'Authority Author', 'collection:AUTHOR', null, null); +INSERT INTO collection VALUES (36, 'Authority Institution', 'collection:INSTITUTION', null, null); +INSERT INTO collection VALUES (37, 'Authority Journal', 'collection:JOURNAL', null, null); +INSERT INTO collection VALUES (38, 'Authority Subject', 'collection:SUBJECT', null, null); INSERT INTO collectiondetailedrecordpagetabs VALUES (8, 'usage;comments;metadata'); INSERT INTO collectiondetailedrecordpagetabs VALUES (19, 'usage;comments;metadata'); @@ -577,6 +582,27 @@ INSERT INTO collectionname VALUES (33,'en','ln','Videos'); INSERT INTO collectionname VALUES (33,'fr','ln','Vidéos'); INSERT INTO collectionname VALUES (33,'it','ln','Filmati'); +INSERT INTO collectionname VALUES (34,'en','ln','Authority Records'); +INSERT INTO collectionname VALUES (34,'fr','ln','Notices d''autorité'); +INSERT INTO collectionname VALUES (34,'pl','ln','Rekordy kontrolne'); + +INSERT INTO collectionname VALUES (35,'en','ln','Authors'); +INSERT INTO collectionname VALUES (35,'fr','ln','Auteurs'); +INSERT INTO collectionname VALUES (35,'pl','ln','Autorzy'); + +INSERT INTO collectionname VALUES (36,'en','ln','Institutions'); +INSERT INTO collectionname VALUES (36,'fr','ln','Institutions'); +INSERT INTO collectionname VALUES (36,'pl','ln','Instytucje'); + +INSERT INTO collectionname VALUES (37,'en','ln','Journals'); +INSERT INTO collectionname VALUES (37,'fr','ln','Journals'); +INSERT INTO collectionname VALUES (37,'pl','ln','Czasopisma'); + +INSERT INTO collectionname VALUES (38,'en','ln','Subjects'); +INSERT INTO collectionname VALUES (38,'fr','ln','Sujets'); +INSERT INTO collectionname VALUES (38,'pl','ln','Tematy'); + + INSERT INTO collection_collection VALUES (1,15,'r',60); @@ -609,6 +635,12 @@ INSERT INTO collection_collection VALUES (13,30,'r',20); INSERT INTO collection_collection VALUES (14,27,'r',20); INSERT INTO collection_collection VALUES (14,28,'r',10); INSERT INTO collection_collection VALUES (14,29,'r',10); +INSERT INTO collection_collection VALUES (1,34,'r',25); +INSERT INTO collection_collection VALUES (34,35,'r',4); +INSERT INTO collection_collection VALUES (34,36,'r',3); +INSERT INTO collection_collection VALUES (34,37,'r',2); +INSERT INTO collection_collection VALUES (34,38,'r',1); + @@ -783,6 +815,14 @@ INSERT INTO collection_field_fieldvalue VALUES (3,2,NULL,'soo',40,0); INSERT INTO collection_field_fieldvalue VALUES (3,3,NULL,'soo',30,0); INSERT INTO collection_field_fieldvalue VALUES (3,15,NULL,'soo',20,0); INSERT INTO collection_field_fieldvalue VALUES (3,12,NULL,'soo',10,0); +INSERT INTO collection_field_fieldvalue VALUES (34,33,NULL,'sew',4,0); +INSERT INTO collection_field_fieldvalue VALUES (34,34,NULL,'sew',3,0); +INSERT INTO collection_field_fieldvalue VALUES (34,35,NULL,'sew',2,0); +INSERT INTO collection_field_fieldvalue VALUES (34,36,NULL,'sew',1,0); +INSERT INTO collection_field_fieldvalue VALUES (35,33,NULL,'sew',1,0); +INSERT INTO collection_field_fieldvalue VALUES (36,34,NULL,'sew',1,0); +INSERT INTO collection_field_fieldvalue VALUES (37,35,NULL,'sew',1,0); +INSERT INTO collection_field_fieldvalue VALUES (38,36,NULL,'sew',1,0); INSERT INTO collection_format VALUES (6,1,100); INSERT INTO collection_format VALUES (6,2,90); diff --git a/modules/miscutil/lib/upgrades/invenio_2013_08_20_bibauthority_updates.py b/modules/miscutil/lib/upgrades/invenio_2013_08_20_bibauthority_updates.py new file mode 100644 index 0000000000..9fe3db735e --- /dev/null +++ b/modules/miscutil/lib/upgrades/invenio_2013_08_20_bibauthority_updates.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +## +## This file is part of Invenio. +## Copyright (C) 2012, 2013 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +from invenio.dbquery import run_sql + +depends_on = ['invenio_2013_03_29_idxINDEX_stopwords_update'] + +def info(): + return """Introduces bibauthority module. Adds: + -> new indexes: + authorityauthor + authoritysubject + authorityjournal + authorityinstitution + -> new fields: + authorityauthor + authoritysubject + authorityjournal + authorityinstitution + -> new tags: + authority: main personal name + authority: alternative personal name + authority: personal name from other record + authority: organization main name' + organization alternative name + organization main from other record + authority: uniform title + authority: uniform title alternatives + authority: uniform title from other record + authority: subject from other record + authority: subject alternative name + authority: subject main name + """ + + +def do_upgrade(): + pass + + +def do_upgrade_atlantis(): + #first step: create tables + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD20F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM; """) + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD20R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD21F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD21R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD22F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD22R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD23F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxWORD23R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR20F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR20R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR21F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR21R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR22F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR22R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR23F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPAIR23R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE20F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE20R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE21F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE21R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE22F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE22R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE23F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) + ) ENGINE=MyISAM;""") + run_sql("""CREATE TABLE IF NOT EXISTS idxPHRASE23R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) + ) ENGINE=MyISAM;""") + #second step: fill tables with data + run_sql("""INSERT INTO field VALUES (33,'authority author','authorityauthor')""") + run_sql("""INSERT INTO field VALUES (34,'authority institution','authorityinstitution')""") + run_sql("""INSERT INTO field VALUES (35,'authority journal','authorityjournal')""") + run_sql("""INSERT INTO field VALUES (36,'authority subject','authoritysubject')""") + run_sql("""INSERT INTO field_tag VALUES (33,1,100)""") + run_sql("""INSERT INTO field_tag VALUES (33,146,100)""") + run_sql("""INSERT INTO field_tag VALUES (33,140,100)""") + run_sql("""INSERT INTO field_tag VALUES (34,148,100)""") + run_sql("""INSERT INTO field_tag VALUES (34,149,100)""") + run_sql("""INSERT INTO field_tag VALUES (34,150,100)""") + run_sql("""INSERT INTO field_tag VALUES (35,151,100)""") + run_sql("""INSERT INTO field_tag VALUES (35,152,100)""") + run_sql("""INSERT INTO field_tag VALUES (35,153,100)""") + run_sql("""INSERT INTO field_tag VALUES (36,154,100)""") + run_sql("""INSERT INTO field_tag VALUES (36,155,100)""") + run_sql("""INSERT INTO field_tag VALUES (36,156,100)""") + run_sql("""INSERT INTO tag VALUES (145,'authority: main personal name','100__a')""") + run_sql("""INSERT INTO tag VALUES (146,'authority: alternative personal name','400__a')""") + run_sql("""INSERT INTO tag VALUES (147,'authority: personal name from other record','500__a')""") + run_sql("""INSERT INTO tag VALUES (148,'authority: organization main name','110__a')""") + run_sql("""INSERT INTO tag VALUES (149,'organization alternative name','410__a')""") + run_sql("""INSERT INTO tag VALUES (150,'organization main from other record','510__a')""") + run_sql("""INSERT INTO tag VALUES (151,'authority: uniform title','130__a')""") + run_sql("""INSERT INTO tag VALUES (152,'authority: uniform title alternatives','430__a')""") + run_sql("""INSERT INTO tag VALUES (153,'authority: uniform title from other record','530__a')""") + run_sql("""INSERT INTO tag VALUES (154,'authority: subject from other record','150__a')""") + run_sql("""INSERT INTO tag VALUES (155,'authority: subject alternative name','450__a')""") + run_sql("""INSERT INTO tag VALUES (156,'authority: subject main name','550__a')""") + + run_sql("""INSERT INTO idxINDEX VALUES (20,'authorityauthor','This index contains words/phrases from author authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexAuthorTokenizer')""") + run_sql("""INSERT INTO idxINDEX VALUES (21,'authorityinstitution','This index contains words/phrases from institution authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer')""") + run_sql("""INSERT INTO idxINDEX VALUES (22,'authorityjournal','This index contains words/phrases from journal authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer')""") + run_sql("""INSERT INTO idxINDEX VALUES (23,'authoritysubject','This index contains words/phrases from subject authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer')""") + + run_sql("""INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (20,33)""") + run_sql("""INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (21,34)""") + run_sql("""INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (22,35)""") + run_sql("""INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (23,36)""") + + +def estimate(): + return 1 + + +def pre_upgrade(): + pass + + +def post_upgrade(): + pass diff --git a/modules/miscutil/sql/tabbibclean.sql b/modules/miscutil/sql/tabbibclean.sql index cbacba3113..e0703c5a1c 100644 --- a/modules/miscutil/sql/tabbibclean.sql +++ b/modules/miscutil/sql/tabbibclean.sql @@ -219,6 +219,10 @@ TRUNCATE idxWORD16F; TRUNCATE idxWORD17F; TRUNCATE idxWORD18F; TRUNCATE idxWORD19F; +TRUNCATE idxWORD20F; +TRUNCATE idxWORD21F; +TRUNCATE idxWORD22F; +TRUNCATE idxWORD23F; TRUNCATE idxWORD01R; TRUNCATE idxWORD02R; TRUNCATE idxWORD03R; @@ -238,6 +242,10 @@ TRUNCATE idxWORD16R; TRUNCATE idxWORD17R; TRUNCATE idxWORD18R; TRUNCATE idxWORD19R; +TRUNCATE idxWORD20R; +TRUNCATE idxWORD21R; +TRUNCATE idxWORD22R; +TRUNCATE idxWORD23R; TRUNCATE idxPAIR01F; TRUNCATE idxPAIR02F; TRUNCATE idxPAIR03F; @@ -257,6 +265,10 @@ TRUNCATE idxPAIR16F; TRUNCATE idxPAIR17F; TRUNCATE idxPAIR18F; TRUNCATE idxPAIR19F; +TRUNCATE idxPAIR20F; +TRUNCATE idxPAIR21F; +TRUNCATE idxPAIR22F; +TRUNCATE idxPAIR23F; TRUNCATE idxPAIR01R; TRUNCATE idxPAIR02R; TRUNCATE idxPAIR03R; @@ -276,6 +288,10 @@ TRUNCATE idxPAIR16R; TRUNCATE idxPAIR17R; TRUNCATE idxPAIR18R; TRUNCATE idxPAIR19R; +TRUNCATE idxPAIR20R; +TRUNCATE idxPAIR21R; +TRUNCATE idxPAIR22R; +TRUNCATE idxPAIR23R; TRUNCATE idxPHRASE01F; TRUNCATE idxPHRASE02F; TRUNCATE idxPHRASE03F; @@ -295,6 +311,10 @@ TRUNCATE idxPHRASE16F; TRUNCATE idxPHRASE17F; TRUNCATE idxPHRASE18F; TRUNCATE idxPHRASE19F; +TRUNCATE idxPHRASE20F; +TRUNCATE idxPHRASE21F; +TRUNCATE idxPHRASE22F; +TRUNCATE idxPHRASE23F; TRUNCATE idxPHRASE01R; TRUNCATE idxPHRASE02R; TRUNCATE idxPHRASE03R; @@ -314,6 +334,10 @@ TRUNCATE idxPHRASE16R; TRUNCATE idxPHRASE17R; TRUNCATE idxPHRASE18R; TRUNCATE idxPHRASE19R; +TRUNCATE idxPHRASE20R; +TRUNCATE idxPHRASE21R; +TRUNCATE idxPHRASE22R; +TRUNCATE idxPHRASE23R; TRUNCATE rnkMETHODDATA; TRUNCATE rnkCITATIONDATA; TRUNCATE rnkCITATIONDATAEXT; diff --git a/modules/miscutil/sql/tabcreate.sql b/modules/miscutil/sql/tabcreate.sql index 135b2fcf12..362da610ae 100644 --- a/modules/miscutil/sql/tabcreate.sql +++ b/modules/miscutil/sql/tabcreate.sql @@ -2060,6 +2060,67 @@ CREATE TABLE IF NOT EXISTS idxWORD19R ( PRIMARY KEY (id_bibrec,type) ) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS idxWORD20F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxWORD20R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + + +CREATE TABLE IF NOT EXISTS idxWORD21F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxWORD21R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxWORD22F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxWORD22R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxWORD23F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(50) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxWORD23R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + CREATE TABLE IF NOT EXISTS idxPAIR01F ( id mediumint(9) unsigned NOT NULL auto_increment, term varchar(100) default NULL, @@ -2345,6 +2406,66 @@ CREATE TABLE IF NOT EXISTS idxPAIR19R ( PRIMARY KEY (id_bibrec,type) ) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS idxPAIR20F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR20R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR21F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR21R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR22F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR22R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR23F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term varchar(100) default NULL, + hitlist longblob, + PRIMARY KEY (id), + UNIQUE KEY term (term) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPAIR23R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + CREATE TABLE IF NOT EXISTS idxPHRASE01F ( id mediumint(9) unsigned NOT NULL auto_increment, term text default NULL, @@ -2630,6 +2751,67 @@ CREATE TABLE IF NOT EXISTS idxPHRASE19R ( PRIMARY KEY (id_bibrec,type) ) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE20F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE20R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE21F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE21R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE22F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE22R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE23F ( + id mediumint(9) unsigned NOT NULL auto_increment, + term text default NULL, + hitlist longblob, + PRIMARY KEY (id), + KEY term (term(50)) +) ENGINE=MyISAM; + +CREATE TABLE IF NOT EXISTS idxPHRASE23R ( + id_bibrec mediumint(9) unsigned NOT NULL, + termlist longblob, + type enum('CURRENT','FUTURE','TEMPORARY') NOT NULL default 'CURRENT', + PRIMARY KEY (id_bibrec,type) +) ENGINE=MyISAM; + -- tables for ranking: CREATE TABLE IF NOT EXISTS rnkMETHOD ( diff --git a/modules/miscutil/sql/tabdrop.sql b/modules/miscutil/sql/tabdrop.sql index f31229d009..93ccce686f 100644 --- a/modules/miscutil/sql/tabdrop.sql +++ b/modules/miscutil/sql/tabdrop.sql @@ -241,6 +241,10 @@ DROP TABLE IF EXISTS idxWORD16F; DROP TABLE IF EXISTS idxWORD17F; DROP TABLE IF EXISTS idxWORD18F; DROP TABLE IF EXISTS idxWORD19F; +DROP TABLE IF EXISTS idxWORD20F; +DROP TABLE IF EXISTS idxWORD21F; +DROP TABLE IF EXISTS idxWORD22F; +DROP TABLE IF EXISTS idxWORD23F; DROP TABLE IF EXISTS idxWORD01R; DROP TABLE IF EXISTS idxWORD02R; DROP TABLE IF EXISTS idxWORD03R; @@ -260,6 +264,10 @@ DROP TABLE IF EXISTS idxWORD16R; DROP TABLE IF EXISTS idxWORD17R; DROP TABLE IF EXISTS idxWORD18R; DROP TABLE IF EXISTS idxWORD19R; +DROP TABLE IF EXISTS idxWORD20R; +DROP TABLE IF EXISTS idxWORD21R; +DROP TABLE IF EXISTS idxWORD22R; +DROP TABLE IF EXISTS idxWORD23R; DROP TABLE IF EXISTS idxPAIR01F; DROP TABLE IF EXISTS idxPAIR02F; DROP TABLE IF EXISTS idxPAIR03F; @@ -279,6 +287,10 @@ DROP TABLE IF EXISTS idxPAIR16F; DROP TABLE IF EXISTS idxPAIR17F; DROP TABLE IF EXISTS idxPAIR18F; DROP TABLE IF EXISTS idxPAIR19F; +DROP TABLE IF EXISTS idxPAIR20F; +DROP TABLE IF EXISTS idxPAIR21F; +DROP TABLE IF EXISTS idxPAIR22F; +DROP TABLE IF EXISTS idxPAIR23F; DROP TABLE IF EXISTS idxPAIR01R; DROP TABLE IF EXISTS idxPAIR02R; DROP TABLE IF EXISTS idxPAIR03R; @@ -298,6 +310,10 @@ DROP TABLE IF EXISTS idxPAIR16R; DROP TABLE IF EXISTS idxPAIR17R; DROP TABLE IF EXISTS idxPAIR18R; DROP TABLE IF EXISTS idxPAIR19R; +DROP TABLE IF EXISTS idxPAIR20R; +DROP TABLE IF EXISTS idxPAIR21R; +DROP TABLE IF EXISTS idxPAIR22R; +DROP TABLE IF EXISTS idxPAIR23R; DROP TABLE IF EXISTS idxPHRASE01F; DROP TABLE IF EXISTS idxPHRASE02F; DROP TABLE IF EXISTS idxPHRASE03F; @@ -317,6 +333,10 @@ DROP TABLE IF EXISTS idxPHRASE16F; DROP TABLE IF EXISTS idxPHRASE17F; DROP TABLE IF EXISTS idxPHRASE18F; DROP TABLE IF EXISTS idxPHRASE19F; +DROP TABLE IF EXISTS idxPHRASE20F; +DROP TABLE IF EXISTS idxPHRASE21F; +DROP TABLE IF EXISTS idxPHRASE22F; +DROP TABLE IF EXISTS idxPHRASE23F; DROP TABLE IF EXISTS idxPHRASE01R; DROP TABLE IF EXISTS idxPHRASE02R; DROP TABLE IF EXISTS idxPHRASE03R; @@ -336,6 +356,10 @@ DROP TABLE IF EXISTS idxPHRASE16R; DROP TABLE IF EXISTS idxPHRASE17R; DROP TABLE IF EXISTS idxPHRASE18R; DROP TABLE IF EXISTS idxPHRASE19R; +DROP TABLE IF EXISTS idxPHRASE20R; +DROP TABLE IF EXISTS idxPHRASE21R; +DROP TABLE IF EXISTS idxPHRASE22R; +DROP TABLE IF EXISTS idxPHRASE23R; DROP TABLE IF EXISTS rnkMETHOD; DROP TABLE IF EXISTS rnkMETHODNAME; DROP TABLE IF EXISTS rnkMETHODDATA; diff --git a/modules/miscutil/sql/tabfill.sql b/modules/miscutil/sql/tabfill.sql index 5c4f5c3f8d..cc73310feb 100644 --- a/modules/miscutil/sql/tabfill.sql +++ b/modules/miscutil/sql/tabfill.sql @@ -57,6 +57,10 @@ INSERT INTO field VALUES (29,'exact first author','exactfirstauthor'); INSERT INTO field VALUES (30,'author count','authorcount'); INSERT INTO field VALUES (31,'reference to','rawref'); INSERT INTO field VALUES (32,'exact title','exacttitle'); +INSERT INTO field VALUES (33,'authority author','authorityauthor'); +INSERT INTO field VALUES (34,'authority institution','authorityinstitution'); +INSERT INTO field VALUES (35,'authority journal','authorityjournal'); +INSERT INTO field VALUES (36,'authority subject','authoritysubject'); INSERT INTO field_tag VALUES (1,100,10); INSERT INTO field_tag VALUES (1,102,10); @@ -201,6 +205,20 @@ INSERT INTO field_tag VALUES (30,2,90); INSERT INTO field_tag VALUES (32,3,100); INSERT INTO field_tag VALUES (32,4,90); +INSERT INTO field_tag VALUES (33,1,100); +INSERT INTO field_tag VALUES (33,146,100); +INSERT INTO field_tag VALUES (33,140,100); +INSERT INTO field_tag VALUES (34,148,100); +INSERT INTO field_tag VALUES (34,149,100); +INSERT INTO field_tag VALUES (34,150,100); +INSERT INTO field_tag VALUES (35,151,100); +INSERT INTO field_tag VALUES (35,152,100); +INSERT INTO field_tag VALUES (35,153,100); +INSERT INTO field_tag VALUES (36,154,100); +INSERT INTO field_tag VALUES (36,155,100); +INSERT INTO field_tag VALUES (36,156,100); + + INSERT INTO format (id,name,code,description,content_type,visibility) VALUES (1,'HTML brief','hb', 'HTML brief output format, used for search results pages.', 'text/html', 1); INSERT INTO format (id,name,code,description,content_type,visibility) VALUES (2,'HTML detailed','hd', 'HTML detailed output format, used for Detailed record pages.', 'text/html', 1); INSERT INTO format (id,name,code,description,content_type,visibility) VALUES (3,'MARC','hm', 'HTML MARC.', 'text/html', 1); @@ -374,6 +392,18 @@ INSERT INTO tag VALUES (141,'title','245__a'); INSERT INTO tag VALUES (142,'main abstract','245__a'); INSERT INTO tag VALUES (143,'internal notes','595__a'); INSERT INTO tag VALUES (144,'other relationship entry', '787%'); +-- INSERT INTO tag VALUES (145,'authority: main personal name','100__a'); -- already exists under a different name ('first author name') +INSERT INTO tag VALUES (146,'authority: alternative personal name','400__a'); +-- INSERT INTO tag VALUES (147,'authority: personal name from other record','500__a'); -- already exists under a different name ('comment') +INSERT INTO tag VALUES (148,'authority: organization main name','110__a'); +INSERT INTO tag VALUES (149,'organization alternative name','410__a'); +INSERT INTO tag VALUES (150,'organization main from other record','510__a'); +INSERT INTO tag VALUES (151,'authority: uniform title','130__a'); +INSERT INTO tag VALUES (152,'authority: uniform title alternatives','430__a'); +INSERT INTO tag VALUES (153,'authority: uniform title from other record','530__a'); +INSERT INTO tag VALUES (154,'authority: subject from other record','150__a'); +INSERT INTO tag VALUES (155,'authority: subject alternative name','450__a'); +INSERT INTO tag VALUES (156,'authority: subject main name','550__a'); INSERT INTO idxINDEX VALUES (1,'global','This index contains words/phrases from global fields.','0000-00-00 00:00:00', '', 'native', 'INDEX-SYNONYM-TITLE,exact','No','No','No','BibIndexDefaultTokenizer'); INSERT INTO idxINDEX VALUES (2,'collection','This index contains words/phrases from collection identifiers fields.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer'); @@ -394,6 +424,11 @@ INSERT INTO idxINDEX VALUES (16,'firstauthor','This index contains fuzzy words/p INSERT INTO idxINDEX VALUES (17,'exactfirstauthor','This index contains exact words/phrases from first author field.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexExactAuthorTokenizer'); INSERT INTO idxINDEX VALUES (18,'authorcount','This index contains number of authors of the record.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexAuthorCountTokenizer'); INSERT INTO idxINDEX VALUES (19,'exacttitle','This index contains exact words/phrases from title fields.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer'); +INSERT INTO idxINDEX VALUES (20,'authorityauthor','This index contains words/phrases from author authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexAuthorTokenizer'); +INSERT INTO idxINDEX VALUES (21,'authorityinstitution','This index contains words/phrases from institution authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer'); +INSERT INTO idxINDEX VALUES (22,'authorityjournal','This index contains words/phrases from journal authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer'); +INSERT INTO idxINDEX VALUES (23,'authoritysubject','This index contains words/phrases from subject authority records.','0000-00-00 00:00:00', '', 'native', '','No','No','No', 'BibIndexDefaultTokenizer'); + INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (1,1); @@ -415,6 +450,10 @@ INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (16,28); INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (17,29); INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (18,30); INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (19,32); +INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (20,33); +INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (21,34); +INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (22,35); +INSERT INTO idxINDEX_field (id_idxINDEX, id_field) VALUES (23,36); INSERT INTO sbmACTION VALUES ('Submit New Record','SBI','running','1998-08-17','2001-08-08','','Submit New Record'); INSERT INTO sbmACTION VALUES ('Modify Record','MBI','modify','1998-08-17','2001-11-07','','Modify Record'); diff --git a/modules/webhelp/web/admin/admin.webdoc b/modules/webhelp/web/admin/admin.webdoc index be0c7dde3c..74b92d7969 100644 --- a/modules/webhelp/web/admin/admin.webdoc +++ b/modules/webhelp/web/admin/admin.webdoc @@ -215,6 +215,23 @@ data are uploaded in Invenio, you may want to modify them via + + + + BibAuthority Admin + + + Enables you to configure Authority Control in Invenio. + + + None + + + Authority Record Admin Guide + + + +

Data provision related modules

diff --git a/modules/webhelp/web/admin/howto/howto-authority-1.png b/modules/webhelp/web/admin/howto/howto-authority-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a8b16c8a1c33b9b33b34fd0231a1faf2769f90a3 GIT binary patch literal 44814 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 Yk4#XPZ=E-E`wjmrOl?fAk=&yG2c@7GGXMYp literal 0 HcmV?d00001 diff --git a/modules/webhelp/web/admin/howto/howto-authority-2.png b/modules/webhelp/web/admin/howto/howto-authority-2.png new file mode 100644 index 0000000000000000000000000000000000000000..2a081126d1e85b957809a1db9c582fe4e1b987a9 GIT binary patch literal 46413 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 qz66Dy8&!u`y!uo}_(mt^^`@xuN8YQ<|H=Q8r z_1v(sefF%Fhep5n?D6r#0%qH`44(?x3kQ0%_eoI?KxCOVs~ULW!!O=EFTPeViH#6@YB z#7tEZ`DI1Stn1ZA&vN2){*T{_*&8nHf*&d>hCRYnrWP3TOR}FRaqVGX)Wu+p?~04c zQNj>d_R!vhh;`?@gLip*X=N$KxM*4aWKIrSq`Gwz4_$G~BIM;V0`WHWu`t1;Q+Hfq zSvLM_U(v_jFr_8XO5!yEd9WE!H=wa$ki6Lb5g#E!^4VU3{HaC=*7BXaf+;1!*?0RF zuhyRKv`yAZADsXU_`_gmhRJ>>W(dF18m|lQRMo%yto-=$B3g{7f;hMgLH{gRi7DK_g44RBsBTS{UU7LBAzUN)PG#$D+|WlXJvm1U!>y@9-T zhVxmzvxJctJl-*SaVK+RwKo}X+gq`yixS%2^)zIofigNx;#rVSwWNnM_Jmo~>{}PxEoo|7ofQ|}Vmtx!Ib0ITzYKmeDNPOPNs7u5NK~E3 zr$=E+<+Tw08unU9`Ko?uV_!ep9VEtmc{ZpHV;<{O#tcSz01*$lS${7jq>TmzJ$cyxc>l*GiuWe67s zM~R|>?9`O7i|X~q54iFeYc)6)_(2*(pXr${nT8lX!`qcKGdri|71 ziL?tu41SgWGov0D;w(jTZm-ZFMXD>8;PlyQE&Aj=4~%MMIi2Yz5A2s75jpt>^NQ%+ zDCJnLBdM&-9%T|v?EV>^9@rVF3EH{&#a70p%S}bTE#Xy2`hnl9Szcp4VVXDls@i}U zXo?&ODqzJnhT0_>wt-kyh2=A#6_U{O*fQp2U%V_wpnE?GuVzxa8#2TFW&Qg@y%EL0(QN z9WM?RNg5(t*t;g!X9=h(+rA=rux8i_L|bT#n2f$KZ_&b68mo{iM~*Yw2NJiKzkATu zo_Sl6xeA#Gfici|(=Y$gV_fa~B^pEN$vj%g}zbY$i_ zI6U-rXgXvxkKMKtETl@ES%x0%8qR&I1!RHC44;3DPvUgIoZ@p&XgB#?rN z*J&4mRDaVNQNQ9qew&;X>OPVH{Y>0uV|#Vka~xE*GV(+*z@2dG z!MCYFE^kMf=z|n-p}wP;jn~k6__I z-acKcc7~!OjrhNeY*Soczjc`3g@%cvre8{-PG9mN$cbBx#o;c4luIt*npaG7y{=8< zOBS2H_xG`yI_9``Hl!PAta-vgy+f3b9P#5HgH@ks2U)~CcbKrIoA+icU}F`)Q=?aI zILDV}=XIFYftcK1NsO+FsgoS{Kdo1h934WR_^YL_rK2G$qoqW8hNdqao6Jt9e_SD_1HsNX*d@-=oUh2|2ce9F$ zO{c#|Pbf-3dB7PC)eeQ>N%?^yy*@Kzk4f$URtrrALsGxUDgk4LQ$;Uimax9JC>xIA zEU0y7e|GuT_EEmB+xPOUa=y$OD9B;7t^(AYnN<#y($Z;a(ZtJ?=7uqU4Z844x+>eD z-HJ^iM9JSD7e!wZi-B~sv!k6L!kAwP`tGl!pl#f%iQM1u;F-O+$KbO7XHrwAcztAe z^PcT~wl2P@+XD0RzC?=N;e|Sh)X63L(TuRqa4bt^g%NhFCO5vu2}Zz_ViJF236$sN z)3{b`#m!=y>S+C)JSX8v-M1xSowV2NG?(vCJrbO5_8OA?hdVVFH0x}F=a1WQ4)`)) z@90lVyJ2>z6%BEX6Irn*_1d{xvIF{J0bx>S%0DQ7|{w89W}HZLc=qyJp%QRZXF2FQ!tN%gBO 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 literal 0 HcmV?d00001 diff --git a/modules/webhelp/web/admin/howto/howto-authority.webdoc b/modules/webhelp/web/admin/howto/howto-authority.webdoc new file mode 100644 index 0000000000..78a2b1491f --- /dev/null +++ b/modules/webhelp/web/admin/howto/howto-authority.webdoc @@ -0,0 +1,99 @@ +## -*- mode: html; coding: utf-8; -*- + +## This file is part of Invenio. +## Copyright (C) 2007, 2008, 2009, 2010, 2011 CERN. +## +## Invenio is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Invenio is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Invenio; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + + + + + +

Introduction

+

This page describes how to use Authority Control in Invenio from a user's perspective

+

For an explanation of how to configure Authority Control in Invenio, cf. _(BibAuthority Admin Guide)_.

+ +

How to MARC authority records

+ +

1. The 980 field

+

When adding an authority record to INVENIO, whether by uploading a MARC record manually or by adding a new record in BibEdit, it is important to add two separate '980' fields to the record. +The first field will contain the value “AUTHORITY” in the $a subfield. +This is to tell INVENIO that this is an authority record. +The second '980' field will likewise contain a value in its $a subfield, only this time you must specify what kind of authority record it is. +Typically an author authority record would contain the term “AUTHOR”, an institution would contain “INSTITUTION” etc. +It is important to communicate these exact terms to the INVENIO admin who will configure how INVENIO handles each of these authority record types for the individual INVENIO modules. +

+ +

2. The 035 field

+

Further, you must add a unique control number to each authority record. In Invenio, this number must be contained in the 035__ $a field of the authority record and contains the MARC code (enclosed in parentheses) of the organization originating the system control number, followed immediately by the number, e.g. "(SzGeCERN)abc123"). +Cf. 035 - System Control Number from the MARC 21 reference page. +

+ +

3. Links between MARC records

+

When creating links between MARC records, we must distinguish two cases: 1) references from bibliographic records towards authority records, and 2) references between authority records
+ +

3.1 Creating a reference from a bibliographic record

+

Example: You have an article (bibliographic record) with Author "Ellis" in the 100__ $a field and you want to create a reference to the authority record for this author.

+

This can be done by inserting the control number of this authority record (as contained in the 035__ $a subfield of the authority record) into the $0 subfield of the same 100__ field of the bibliographic record, prefixed by the type of authority record being referenced and a (configurable) separator. +

+

A 100 field might look like this:

+
+100__ $a Ellis, J.
+      $0 AUTHOR:(CERN)abc123
+      $u CERN
+      $0 INSTITUTION:(CERN)xyz789
+
+

In this case, since we are referencing an AUTHOR authority record, the 100__ $0 subfield would read, e.g. "AUTHOR:(CERN)abc123". If you want to reference an institution, e.g. SLAC, as affiliation for an author, you would prefix the control number with "INSTITUTION". You would add another 100__ $0 subfield to the same 100 field and add the value "INSTITUTION:(CERN)xyz789".

+ +

3.2 Creating links between authority records

+

Links between authority records use the 5xx fields. AUTHOR records use the 500 fields, INSTITUTION records the 510 fields and so on, according to the MARC 21 standard. +

+
Subfield codes:
+
+$a - Corporate name or jurisdiction name as entry element (NR)
+     e.g. "SLAC National Accelerator Laboratory" or "European Organization for Nuclear Research"
+
+$w - Control subfield (NR)
+     'a' - for predecessor
+     'b' - for successor
+     't' - for top / parent
+
+$4 - Relationship code (R)
+     The control number of the referenced authority record, 
+     e.g. "(CERN)iii000"
+
+ +

Example: You want to add a predecessor to an INSTITUTION authority record. Let's say "Institution A" has control number "(CERN)iii000" and its successor "Institution B" has control number "(CERN)iii001". In order to designate Institution A as predecessor of Institution B, we would add a 510 field to Institution B with a $w value of 'a', a $a value of 'Institution A', and a $4 value of '(CERN)iii000' like this: +

+510__ $a Institution A
+      $w a
+      $4 INSTITUTION:(CERN)iii000
+
+ +

4. Other MARC for authority records

+

All other MARC fields should follow the MARC 21 Format for Authority Data

+ +

Creating collections of authority records

+

Once the authority records have been given the appropriate '980__a' values (cf. above), creating a collection of authority records is no different from creating any other collection in INVENIO. You can simply define a new collection defined by the usual collection query 'collection:AUTHOR' for author authority records, or 'collection:INSTITUTION' for institutions, etc.

+

The recommended way of creating collections for authority records is to create a “virtual collection” for the main 'collection:AUTHORITY' collection and then add the individual authority record collections as regular children of this collection. This will allow you to browse and search within authority records without making this the default for all INVENIO searches.

+ +

How to use authority control in BibEdit

+

When using BibEdit to modify MARC meta-data of bibliographic records, certain fields may be configured (by the admin of your INVENIO installation) to offer you auto-complete functionality based upon the data contained in authority records for that field. For example, if MARC subfield 100__ $a was configured to be under authority control, then typing the beginning of a word into this subfield will trigger a drop-down list, offering you a choice of values to choose from. When you click on one of the entries in the drop-down list, this will not only populate the immediate subfield you are editing, but it will also insert a reference into a new $0 subfield of the same MARC field you are editing. This reference tells the system that the author you are referring to is the author as contained in the 'author' authority record with the given authority record control number.

+

The illustration below demonstrates how this works:

+autosuggest dropdown +

Typing “Elli” into the 100__ $a subfield will present you with a list of authors that contain a word starting with “Elli” somewhere in their name. In case there are multiple authors with similar or identical names (as is the case in the example shown here), you will receive additional information about these authors to help you disambiguate. The fields to be used for disambiguation can be configured by your INVENIO administrator. If such fields have not been configured, or if they are not sufficient for disambiguation, the authority record control number will be used to assure a unique value for each entry in the drop-down list. In the example above, the first author can be uniquely identified by his email address, whereas for the latter we have only the authority record control number as uniquely identifying characteristic.

+inserted $0 subfield for authority record +

If in the shown example you click on the first author from the list, this author's name will automatically be inserted into the 100__ $a subfield you were editing, while the authority type and the authority record control number “author:(SzGeCERN)abc123” , is inserted into a new $0 subfield (cf. Illustration 2). This new subfield tells INVENIO that “Ellis, John” is associated with the 'author' authority record containing the authority record control number “(SzGeCERN)abc123”. In this example you can also see that the author's affiliation has been entered in the same way as well, using the auto-complete option for the 100__ $u subfield. In this case the author's affiliation is the “University of Oxford”, which is associated in this INVENIO installation with the 'institution' authority record containing the authority record control number “(SzGeCERN)inst0001”.

+

If INVENIO has no authority record data to match what you type into the authority-controlled subfield, you still have the possibility to enter a value manually.

\ No newline at end of file diff --git a/modules/webhelp/web/admin/howto/howto-marc.webdoc b/modules/webhelp/web/admin/howto/howto-marc.webdoc index 584165948c..a6cb970566 100644 --- a/modules/webhelp/web/admin/howto/howto-marc.webdoc +++ b/modules/webhelp/web/admin/howto/howto-marc.webdoc @@ -21,6 +21,16 @@ +

Overview

+ +

This HOWTO guide intends to explain how to use the MARC 21 +format for bibliographic records in Invenio.

+ +

For an explanation of how to use MARC in authority records, +please read How to MARC +authority records +

+

Why to MARC at all?

All the bibliographic data in the Invenio system are diff --git a/modules/webhelp/web/admin/howto/howto.webdoc b/modules/webhelp/web/admin/howto/howto.webdoc index 1d914af507..a936ec9d19 100644 --- a/modules/webhelp/web/admin/howto/howto.webdoc +++ b/modules/webhelp/web/admin/howto/howto.webdoc @@ -46,6 +46,11 @@ care of its normal operation day by day.

Describes how to manipulate fulltext files within your Invenio installation. + +
_(HOWTO Manage Authority Records)_ + +
Describes how to manage Authority Records within your Invenio installation. + diff --git a/modules/webhelp/web/hacking/hacking.webdoc b/modules/webhelp/web/hacking/hacking.webdoc index c8095dda18..702ea1dfdc 100644 --- a/modules/webhelp/web/hacking/hacking.webdoc +++ b/modules/webhelp/web/hacking/hacking.webdoc @@ -64,6 +64,10 @@ wiki.

Describes information useful to understand how BibAuthorID works.
+
BibAuthority Internals
+
Describes information useful to understand how BibAuthority works. +
+
BibClassify Internals
Describes information useful to understand how BibClassify works, the taxonomy extensions we use, how the keyword extraction algorithm works. diff --git a/modules/websearch/lib/search_engine.py b/modules/websearch/lib/search_engine.py index 0f301873de..d251b8b64d 100644 --- a/modules/websearch/lib/search_engine.py +++ b/modules/websearch/lib/search_engine.py @@ -801,7 +801,7 @@ def create_basic_search_units(req, p, f, m=None, of='hb'): if f and p[0] == '"' and p[-1] == '"': ## B0 - does 'p' start and end by double quote, and is 'f' defined? => doing ACC search opfts.append(['+', p[1:-1], f, 'a']) - elif f in ('author', 'firstauthor', 'exactauthor', 'exactfirstauthor') and author_name_requires_phrase_search(p): + elif f in ('author', 'firstauthor', 'exactauthor', 'exactfirstauthor', 'authorityauthor') and author_name_requires_phrase_search(p): ## B1 - do we search in author, and does 'p' contain space/comma/dot/etc? ## => doing washed ACC search opfts.append(['+', p, f, 'a']) @@ -2623,7 +2623,7 @@ def search_unit_in_idxphrases(p, f, type, wl=0): query_params = (p,) # special washing for fuzzy author index: - if f in ('author', 'firstauthor', 'exactauthor', 'exactfirstauthor'): + if f in ('author', 'firstauthor', 'exactauthor', 'exactfirstauthor', 'authorityauthor'): query_params_washed = () for query_param in query_params: query_params_washed += (wash_author_name(query_param),) @@ -4446,9 +4446,47 @@ def print_records(req, recIDs, jrec=1, rg=CFG_WEBSEARCH_DEF_RECORDS_IN_GROUPS, f tabs, ln)) elif tab == 'keywords': - from invenio import bibclassify_webinterface + from invenio.bibclassify_webinterface import \ + record_get_keywords, write_keywords_body, \ + generate_keywords + from invenio.webinterface_handler import wash_urlargd + form = req.form + argd = wash_urlargd(form, { + 'generate': (str, 'no'), + 'sort': (str, 'occurrences'), + 'type': (str, 'tagcloud'), + 'numbering': (str, 'off'), + }) recid = recIDs[irec] - bibclassify_webinterface.main_page(req, recid, tabs, ln, webstyle_templates) + + req.write(webstyle_templates.detailed_record_container_top(recid, + tabs, + ln)) + content = websearch_templates.tmpl_record_plots(recID=recid, + ln=ln) + req.write(content) + req.write(webstyle_templates.detailed_record_container_bottom(recid, + tabs, + ln)) + + req.write(webstyle_templates.detailed_record_container_top(recid, + tabs, ln, citationnum=citedbynum, referencenum=references)) + + if argd['generate'] == 'yes': + # The user asked to generate the keywords. + keywords = generate_keywords(req, recid, argd) + else: + # Get the keywords contained in the MARC. + keywords = record_get_keywords(recid, argd) + + if argd['sort'] == 'related' and not keywords: + req.write('You may want to run BibIndex.') + + # Output the keywords or the generate button. + write_keywords_body(keywords, req, recid, argd) + + req.write(webstyle_templates.detailed_record_container_bottom(recid, + tabs, ln)) elif tab == 'plots': req.write(webstyle_templates.detailed_record_container_top(recIDs[irec], tabs, diff --git a/modules/websearch/lib/websearch_regression_tests.py b/modules/websearch/lib/websearch_regression_tests.py index 574ce1f981..84fbd7c724 100644 --- a/modules/websearch/lib/websearch_regression_tests.py +++ b/modules/websearch/lib/websearch_regression_tests.py @@ -1001,13 +1001,14 @@ def test_nearest_terms_box_in_unsuccessful_structured_query(self): self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=ellisz&f=author', expected_text="Nearest terms in any collection are", - expected_link_target=CFG_SITE_URL+"/search?ln=en&p=fabbro&f=author", - expected_link_label='fabbro')) + expected_link_target=CFG_SITE_URL+"/search?ln=en&p=eisenhandler&f=author", + expected_link_label='eisenhandler')) self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=author%3Aellisz', expected_text="Nearest terms in any collection are", - expected_link_target=CFG_SITE_URL+"/search?ln=en&p=author%3Afabbro", - expected_link_label='fabbro')) + expected_link_target=CFG_SITE_URL+"/search?ln=en&p=author%3Aeisenhandler", + expected_link_label='eisenhandler')) + def test_nearest_terms_box_in_query_with_invalid_index(self): """ websearch - nearest terms box for queries with invalid indexes specified """ @@ -1174,12 +1175,12 @@ def test_search_engine_python_api_for_failed_query(self): def test_search_engine_python_api_for_successful_query(self): """websearch - search engine Python API for successful query""" - self.assertEqual([8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47], + self.assertEqual([8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47, 118], perform_request_search(p='ellis')) def test_search_engine_python_api_ignore_paging_parameter(self): """websearch - search engine Python API for successful query, ignore paging parameters""" - self.assertEqual([8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47], + self.assertEqual([8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47, 118], perform_request_search(p='ellis', rg=5, jrec=3)) def test_search_engine_python_api_respect_sorting_parameter(self): @@ -1893,6 +1894,12 @@ def test_search_engine_python_api_xmlmarc_field_filtered_hidden_guest(self): """) + + def test_search_engine_python_api_long_author_with_quotes(self): + """websearch - search engine Python API for p=author:"Abbot, R B"'""" \ + """this test was written along with a bug report, needs fixing.""" + self.assertEqual([16], perform_request_search(p='author:"Abbott, R B"')) + class WebSearchSearchEngineWebAPITest(unittest.TestCase): """Check typical search engine Web API calls on the demo data.""" @@ -1907,13 +1914,13 @@ def test_search_engine_web_api_for_successful_query(self): """websearch - search engine Web API for successful query""" self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=ellis&of=id', - expected_text="[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47]")) + expected_text="[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47, 118]")) def test_search_engine_web_api_ignore_paging_parameter(self): """websearch - search engine Web API for successful query, ignore paging parameters""" self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=ellis&of=id&rg=5&jrec=3', - expected_text="[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47]")) + expected_text="[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 47, 118]")) def test_search_engine_web_api_respect_sorting_parameter(self): """websearch - search engine Web API for successful query, respect sorting parameters""" @@ -3752,7 +3759,7 @@ class WebSearchSortResultsTest(unittest.TestCase): def test_sort_results_default(self): """websearch - search results sorting, default method""" self.assertEqual([], - test_web_page_content(CFG_SITE_URL + '/search?p=of&f=title&rg=1', + test_web_page_content(CFG_SITE_URL + '/search?p=of&f=title&rg=3', expected_text="CMS animation of the high-energy collisions")) def test_sort_results_ascending(self): @@ -4279,7 +4286,7 @@ def test_span_in_author(self): """websearch - span query in special author index""" self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=author%3A%22Ellis,%20K%22-%3E%22Ellis,%20RZ%22&of=id&ap=0', - expected_text='[8, 11, 13, 17, 47]')) + expected_text='[8, 9, 11, 12, 13, 14, 17, 18, 47]')) class WebSearchReferstoCitedbyTest(unittest.TestCase): @@ -4354,7 +4361,7 @@ def test_and_not_parens(self): 'websearch - find a ellis, j and not a enqvist' self.assertEqual([], test_web_page_content(CFG_SITE_URL +'/search?p=find+a+ellis%2C+j+and+not+a+enqvist&of=id&ap=0', - expected_text='[9, 12, 14, 47]')) + expected_text='[9, 12, 14, 47, 118]')) if DATEUTIL_AVAILABLE: def test_dadd_search(self): @@ -4363,7 +4370,7 @@ def test_dadd_search(self): # should return every document in the system self.assertEqual([], test_web_page_content(CFG_SITE_URL +'/search?ln=en&p=find+da+%3E+today+-+3650&f=&of=id', - expected_text='[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100, 101, 102, 103, 104, 107, 108, 113]')) + expected_text='[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100, 101, 102, 103, 104, 107, 108, 113, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141]')) class WebSearchDateQueryTest(unittest.TestCase): @@ -4473,7 +4480,7 @@ def test_journal_authorcount_span(self): """websearch - author count, span query""" self.assertEqual([], test_web_page_content(CFG_SITE_URL + '/search?p=authorcount%3A9-%3E16&of=id', - expected_text="[69, 71]")) + expected_text="[69, 71, 127]")) def test_journal_authorcount_plus(self): """websearch - author count, plus query""" @@ -4481,6 +4488,7 @@ def test_journal_authorcount_plus(self): test_web_page_content(CFG_SITE_URL + '/search?p=50%2B&f=authorcount&of=id', expected_text="[10, 17]")) + class WebSearchPerformRequestSearchRefactoringTest(unittest.TestCase): """Tests the perform request search API after refactoring.""" @@ -4514,21 +4522,21 @@ def _run_test(self, test_args, expected_results): def test_queries(self): """websearch - testing p_r_s standard arguments and their combinations""" - self._run_test('p=ellis;f=author;action=Search', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47]) + self._run_test('p=ellis;f=author;action=Search', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47, 118]) - self._run_test('p=ellis;f=author;sf=title;action=Search', [8, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) + self._run_test('p=ellis;f=author;sf=title;action=Search', [118, 8, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) - self._run_test('p=ellis;f=author;sf=title;wl=5;action=Search', [8, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) + self._run_test('p=ellis;f=author;sf=title;wl=5;action=Search', [118, 8, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) - self._run_test('p=ellis;f=author;sf=title;wl=5;so=a', [13, 47, 10, 12, 18, 17, 11, 9, 14, 16, 8]) + self._run_test('p=ellis;f=author;sf=title;wl=5;so=a', [118, 13, 47, 10, 12, 18, 17, 11, 9, 14, 16, 8]) - self._run_test('p=ellis;f=author;sf=title;wl=5;so=d', [8, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) + self._run_test('p=ellis;f=author;sf=title;wl=5;so=d', [118, 8, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) - self._run_test('p=ell*;sf=title;wl=5', [8, 15, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) + self._run_test('p=ell*;sf=title;wl=5', [118, 8, 15, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) self._run_test('p=ell*;sf=title;wl=1', [10]) - self._run_test('p=ell*;sf=title;wl=100', [8, 15, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) + self._run_test('p=ell*;sf=title;wl=100', [118, 8, 15, 16, 14, 9, 11, 17, 18, 12, 10, 47, 13]) self._run_test('p=muon OR kaon;f=author;sf=title;wl=5;action=Search', []) @@ -4544,36 +4552,36 @@ def test_queries(self): # self._run_test('p=el*;rm=citation', [2, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 23, 30, 32, 34, 47, 48, 51, 52, 54, 56, 58, 59, 92, 97, 100, 103, 18, 74, 91, 94, 81]) if not get_external_word_similarity_ranker(): - self._run_test('p=el*;rm=wrd', [2, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 23, 30, 32, 34, 47, 48, 51, 52, 54, 56, 58, 59, 74, 81, 91, 92, 94, 97, 100, 103, 109]) + self._run_test('p=el*;rm=wrd', [2, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 23, 30, 32, 34, 47, 48, 51, 52, 54, 56, 58, 59, 74, 81, 91, 92, 94, 97, 100, 103, 109, 118, 123, 127, 128]) - self._run_test('p=el*;sf=title', [100, 32, 8, 15, 16, 81, 97, 34, 23, 58, 2, 14, 9, 11, 30, 109, 52, 48, 94, 17, 56, 18, 91, 59, 12, 92, 74, 54, 103, 10, 51, 47, 13]) + self._run_test('p=el*;sf=title', [118, 123, 100, 32, 8, 15, 16, 81, 97, 34, 23, 127, 58, 2, 14, 9, 128, 11, 30, 109, 52, 48, 94, 17, 56, 18, 91, 59, 12, 92, 74, 54, 103, 10, 51, 47, 13]) self._run_test('p=boson;rm=citation', [1, 47, 50, 107, 108, 77, 95]) if not get_external_word_similarity_ranker(): self._run_test('p=boson;rm=wrd', [108, 77, 47, 50, 95, 1, 107]) - self._run_test('p1=ellis;f1=author;m1=a;op1=a;p2=john;f2=author;m2=a', []) + self._run_test('p1=ellis;f1=author;m1=a;op1=a;p2=john;f2=author;m2=a', [9, 12, 14, 18, 118]) - self._run_test('p1=ellis;f1=author;m1=o;op1=a;p2=john;f2=author;m2=o', []) + self._run_test('p1=ellis;f1=author;m1=o;op1=a;p2=john;f2=author;m2=o', [9, 12, 14, 18, 118]) self._run_test('p1=ellis;f1=author;m1=e;op1=a;p2=john;f2=author;m2=e', []) - self._run_test('p1=ellis;f1=author;m1=a;op1=o;p2=john;f2=author;m2=a', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47]) + self._run_test('p1=ellis;f1=author;m1=a;op1=o;p2=john;f2=author;m2=a', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47, 118]) - self._run_test('p1=ellis;f1=author;m1=o;op1=o;p2=john;f2=author;m2=o', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47]) + self._run_test('p1=ellis;f1=author;m1=o;op1=o;p2=john;f2=author;m2=o', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47, 118]) self._run_test('p1=ellis;f1=author;m1=e;op1=o;p2=john;f2=author;m2=e', []) - self._run_test('p1=ellis;f1=author;m1=a;op1=n;p2=john;f2=author;m2=a', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47]) + self._run_test('p1=ellis;f1=author;m1=a;op1=n;p2=john;f2=author;m2=a', [8, 10, 11, 13, 16, 17, 47]) - self._run_test('p1=ellis;f1=author;m1=o;op1=n;p2=john;f2=author;m2=o', [8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 47]) + self._run_test('p1=ellis;f1=author;m1=o;op1=n;p2=john;f2=author;m2=o', [8, 10, 11, 13, 16, 17, 47]) self._run_test('p1=ellis;f1=author;m1=e;op1=n;p2=john;f2=author;m2=e', []) - self._run_test('p=Ellis, J;ap=1', [9, 10, 11, 12, 14, 17, 18, 47]) + self._run_test('p=Ellis, J;ap=1', [9, 10, 11, 12, 14, 17, 18, 47, 118]) - self._run_test('p=Ellis, J;ap=0', [9, 10, 11, 12, 14, 17, 18, 47]) + self._run_test('p=Ellis, J;ap=0', [9, 10, 11, 12, 14, 17, 18, 47, 118]) self._run_test('p=recid:148x', []) diff --git a/modules/websearch/lib/websearchadminlib.py b/modules/websearch/lib/websearchadminlib.py index 69eff2176a..8a44dcbda4 100644 --- a/modules/websearch/lib/websearchadminlib.py +++ b/modules/websearch/lib/websearchadminlib.py @@ -2196,8 +2196,7 @@ def create_colltree(tree, col_dict, colID, ln, move_from='', move_to='', rtype=' text += """ """ tables = tables - 1 - text += """ - """ + text += """""" return text diff --git a/modules/webstyle/lib/webstyle_regression_tests.py b/modules/webstyle/lib/webstyle_regression_tests.py index 596dfa368a..175044cd62 100644 --- a/modules/webstyle/lib/webstyle_regression_tests.py +++ b/modules/webstyle/lib/webstyle_regression_tests.py @@ -108,7 +108,7 @@ def test_invalid_external_redirection(self): def test_latest_article_redirection(self): """webstyle - test redirecting to latest article via goto_plugin_latest_record""" register_redirection('latest_article', 'goto_plugin_latest_record', parameters={'cc': 'Articles'}) - self.assertEqual(get_final_url(CFG_SITE_URL + '/goto/latest_article'), CFG_SITE_URL + '/record/108') + self.assertEqual(get_final_url(CFG_SITE_URL + '/goto/latest_article'), CFG_SITE_URL + '/record/128') @nottest def FIXME_TICKET_1293_test_latest_pdf_article_redirection(self):