## May, 2018

Tue May 01, 2018 in Notes

## 2018-05-01

- I cleared the Solr statistics core on DSpace Test by issuing two commands directly to the Solr admin interface:
  - http://localhost:3000/solr/statistics/update?stream.body=%3Cdelete%3E%3Cquery%3E*:*%3C/query%3E%3C/delete%3E
  - http://localhost:3000/solr/statistics/update?stream.body=%3Ccommit/%3E
- Then I reduced the JVM heap size from 6144 back to 5120m
- Also, I switched it to use OpenJDK instead of Oracle Java, as well as re-worked the Ansible infrastructure scripts to support hosts choosing which distribution they want to use

## 2018-05-02

- Advise Fabio Fidanza about integrating CGSpace content in the new CGIAR corporate website
- I think they can mostly rely on using the `cg.contributor.crp` field
- Looking over some IITA records for Sisay
  - Other than trimming and collapsing consecutive whitespace, I made some other corrections
  - I need to check the correct formatting of COTE D’IVOIRE vs COTE D’IVOIRE
  - I replaced all DOIs with HTTPS
  - I checked a few DOIs and found at least one that was missing, so I Googled the title of the paper and found the correct DOI
  - Also, I found an FAQ for DOI that says the `dx.doi.org` syntax is older, so I will replace all the DOIs with `doi.org` instead
  - I found five records with “ISI Jounal” instead of “ISI Journal”
  - I found one item with IITA subject “.”
  - Need to remember to check the facets for things like this in sponsorship:
    - Deutsche Gesellschaft für Internationale Zusammenarbeit
    - Deutsche Gesellschaft fur Internationale Zusammenarbeit
  - Eight records with language “fn” instead of “fr”
  - One incorrect type (lowercase “proceedings”): Conference proceedings
  - Found some capitalized CRPs in `cg.contributor.crp`
  - Found some incorrect author affiliations, ie “Institut de Recherche pour le Developpement Agricolc” should be “Institut de Recherche pour le Developpement Agricole”
  - Wow, and for sponsors there are the following:
    - Incorrect: Flemish Agency for Development Cooperation and Technical Assistance
    - Incorrect: Flemish Organization for Development Cooperation and Technical Assistance
    - Correct: Flemish Association for Development Cooperation and Technical Assistance
  - One item had region “WEST” (I corrected it to “WEST AFRICA”)

## 2018-05-03

- It turns out that the IITA records that I was helping Sisay with in March were imported in 2018-04 without a final check by Abenet or I
- There are lots of errors on language, CRP, and even some encoding errors on abstract fields
- I export them and include the hidden metadata fields like `dc.date.accessioned` so I can filter the ones from 2018-04 and correct them in Open Refine:

    $ dspace metadata-export -a -f /tmp/iita.csv -i 10568/68616

- Abenet sent a list of 46 ORCID identifiers for ILRI authors so I need to get their names using my resolve-orcids.py script and merge them into our controlled vocabulary
- On the messed up IITA records from 2018-04 I see sixty DOIs in incorrect format (cg.identifier.doi)

## 2018-05-06

- Fixing the IITA records from Sisay, sixty DOIs have completely invalid format like `http:dx.doi.org10.1016j.cropro.2008.07.003`
- I corrected all the DOIs and then checked them for validity with a quick bash loop:

    $ for line in $(< /tmp/links.txt); do echo $line; http --print h $line; done

- Most of the links are good, though one is duplicate and one seems to even be incorrect in the publisher’s site so…
- Also, there are some duplicates:
  - `10568/92241` and `10568/92230` (same DOI)
  - `10568/92151` and `10568/92150` (same ISBN)
  - `10568/92291` and `10568/92286` (same citation, title, authors, year)
- Messed up abstracts:
  - `10568/92309`
- Fixed some issues in regions, countries, sponsors, ISSN, and cleaned whitespace errors from citation, abstract, author, and titles
- Fixed all issues with CRPs
- A few more interesting Unicode characters to look for in text fields like author, abstracts, and citations might be: `’` (0x2019), `·` (0x00b7), and `€` (0x20ac)
- A custom text facit in OpenRefine with this GREL expression could be a good for finding invalid characters or encoding errors in authors, abstracts, etc:

    or(
      isNotNull(value.match(/.*[(|)].*/)),
      isNotNull(value.match(/.*\uFFFD.*/)),
      isNotNull(value.match(/.*\u00A0.*/)),
      isNotNull(value.match(/.*\u200A.*/)),
      isNotNull(value.match(/.*\u2019.*/)),
      isNotNull(value.match(/.*\u00b7.*/)),
      isNotNull(value.match(/.*\u20ac.*/))
    )

- I found some more IITA records that Sisay imported on 2018-03-23 that have invalid CRP names, so now I kinda want to check those ones!
- Combine the ORCID identifiers Abenet sent with our existing list and resolve their names using the resolve-orcids.py script:

    $ cat ~/src/git/DSpace/dspace/config/controlled-vocabularies/cg-creator-id.xml /tmp/ilri-orcids.txt | grep -oE '[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}' | sort | uniq > /tmp/2018-05-06-combined.txt
    $ ./resolve-orcids.py -i /tmp/2018-05-06-combined.txt -o /tmp/2018-05-06-combined-names.txt -d
    # sort names, copy to cg-creator-id.xml, add XML formatting, and then format with tidy (preserving accents)
    $ tidy -xml -utf8 -iq -m -w 0 dspace/config/controlled-vocabularies/cg-creator-id.xml

- I made a pull request (#373) for this that I’ll merge some time next week (I’m expecting Atmire to get back to us about DSpace 5.8 soon)
- After testing quickly I just decided to merge it, and I noticed that I don’t even need to restart Tomcat for the changes to get loaded

## 2018-05-07

- I spent a bit of time playing with conciliator and Solr, trying to figure out how to reconcile columns in OpenRefine with data in our existing Solr cores (like CRP subjects)
- The documentation regarding the Solr stuff is limited, and I cannot figure out what all the fields in `conciliator.properties` are supposed to be
- But then I found reconcile-csv, which allows you to reconcile against values in a CSV file!
- That, combined with splitting our multi-value fields on “||” in OpenRefine is amaaaaazing, because after reconciliation you can just join them again
- Oh wow, you can also facet on the individual values once you’ve split them! That’s going to be amazing for proofing CRPs, subjects, etc.

## 2018-05-09

- Udana asked about the Book Chapters we had been proofing on DSpace Test in 2018-04
- I told him that there were still some TODO items for him on that data, for example to update the `dc.language.iso` field for the Spanish items
- I was trying to remember how I parsed the `input-forms.xml` using `xmllint` to extract subjects neatly
- I could use it with reconcile-csv or to populate a Solr instance for reconciliation
- This XPath expression gets close, but outputs all items on one line:

    $ xmllint --xpath '//value-pairs[@value-pairs-name="crpsubject"]/pair/stored-value/node()' dspace/config/input-forms.xml
    Agriculture for Nutrition and HealthBig DataClimate Change, Agriculture and Food SecurityExcellence in BreedingFishForests, Trees and AgroforestryGenebanksGrain Legumes and Dryland CerealsLivestockMaizePolicies, Institutions and MarketsRiceRoots, Tubers and BananasWater, Land and EcosystemsWheatAquatic Agricultural SystemsDryland CerealsDryland SystemsGrain LegumesIntegrated Systems for the Humid TropicsLivestock and Fish

- Maybe `xmlstarlet` is better:

    $ xmlstarlet sel -t -v '//value-pairs[@value-pairs-name="crpsubject"]/pair/stored-value/text()' dspace/config/input-forms.xml
    Agriculture for Nutrition and Health
    Big Data
    Climate Change, Agriculture and Food Security
    Excellence in Breeding
    Fish
    Forests, Trees and Agroforestry
    Genebanks
    Grain Legumes and Dryland Cereals
    Livestock
    Maize
    Policies, Institutions and Markets
    Rice
    Roots, Tubers and Bananas
    Water, Land and Ecosystems
    Wheat
    Aquatic Agricultural Systems
    Dryland Cereals
    Dryland Systems
    Grain Legumes
    Integrated Systems for the Humid Tropics
    Livestock and Fish

- Discuss Colombian BNARS harvesting the CIAT data from CGSpace
- They are using a system called Primo and the only options for data harvesting in that system are via FTP and OAI
- I told them to get all CIAT records via OAI
- Just a note to myself, I figured out how to get reconcile-csv to run from source rather than running the old pre-compiled JAR file:

    $ lein run /tmp/crps.csv name id

- I tried to reconcile against a CSV of our countries but reconcile-csv crashes

## 2018-05-13

- It turns out there was a space in my “country” header that was causing reconcile-csv to crash
- After removing that it works fine!
- Looking at Sisay’s 2,640 CIFOR records on DSpace Test (10568/92904)
  - Trimmed all leading / trailing white space and condensed multiple spaces into one
  - Corrected DOIs to use HTTPS and “doi.org” instead of “dx.doi.org”
    - There are eight items in `cg.identifier.doi` that are not DOIs)
  - Corrected `cg.identifier.url` links to cifor.org to use HTTPS
  - Corrected `dc.language.iso` from vt to vi (Vietnamese)
  - Corrected affiliations to not use acronyms
  - Reconcile countries against our countries list (removing terms like LATIN AMERICA, CENTRAL AFRICA, etc that are not countries)
  - Reconcile regions against our list of regions

## 2018-05-14

- Send a message to the OpenRefine mailing list about the bug with reconciling multi-value cells
- Help Silvia Alonso get a list of all her publications since 2013 from Listings and Reports

## 2018-05-15

- Turns out I was doing the OpenRefine reconciliation wrong: I needed to copy the matched values to a new column!
- Also, I learned how to do something cool with Jython expressions in OpenRefine
- This will fetch a URL and return its HTTP response code:

    import urllib2
    import re

    pattern = re.compile('.*10.1016.*')
    if pattern.match(value):
      get = urllib2.urlopen(value)
      return get.getcode()

    return "blank"

- I used a regex to limit it to just some of the DOIs in this case because there were thousands of URLs
- Here the response code would be 200, 404, etc, or “blank” if there is no URL for that item
- You could use this in a facet or in a new column
- More information and good examples here: https://programminghistorian.org/lessons/fetch-and-parse-data-with-openrefine
- Finish looking at the 2,640 CIFOR records on DSpace Test (10568/92904), cleaning up authors and adding collection mappings
- They can now be moved to CGSpace as far as I’m concerned, but I don’t know if Sisay will do it or me
- I was checking the CIFOR data for duplicates using Atmire’s Metadata Quality Module (and found some duplicates actually), but then DSpace died…
- I didn’t see anything in the Tomcat, DSpace, or Solr logs, but I saw this in `dmest -T`:

    [Tue May 15 12:10:01 2018] Out of memory: Kill process 3763 (java) score 706 or sacrifice child
    [Tue May 15 12:10:01 2018] Killed process 3763 (java) total-vm:14667688kB, anon-rss:5705268kB, file-rss:0kB, shmem-rss:0kB
    [Tue May 15 12:10:01 2018] oom_reaper: reaped process 3763 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

- So the Linux kernel killed Java…
- Maria from Bioversity mailed to say she got an error while submitting an item on CGSpace:

    Unable to load Submission Information, since WorkspaceID (ID:S96060) is not a valid in-process submission

- Looking in the DSpace log I see something related:

    2018-05-15 12:35:30,858 INFO  org.dspace.submit.step.CompleteStep @ m.garruccio@cgiar.org:session_id=8AC4499945F38B45EF7A1226E3042DAE:submission_complete:Completed submission with id=96060

- So I’m not sure…
- I finally figured out how to get OpenRefine to reconcile values from Solr via conciliator:
- The trick was to use a more appropriate Solr fieldType `text_en` instead of `text_general` so that more terms match, for example uppercase and lower case:

    $ ./bin/solr start
    $ ./bin/solr create_core -c countries
    $ curl -X POST -H 'Content-type:application/json' --data-binary '{"add-field": {"name":"country", "type":"text_en", "multiValued":false, "stored":true}}' http://localhost:8983/solr/countries/schema
    $ ./bin/post -c countries ~/src/git/DSpace/2018-05-10-countries.csv

- It still doesn’t catch simple mistakes like “ALBANI” or “AL BANIA” for “ALBANIA”, and it doesn’t return scores, so I have to select matches manually:

- I should probably make a general copy field and set it to be the default search field, like DSpace’s search core does (see schema.xml):

    <defaultSearchField>search_text</defaultSearchField>
    ...
    <copyField source="*" dest="search_text"/>

- Actually, I wonder how much of their schema I could just copy…
- Apparently the default search field is the `df` parameter and you could technically just add it to the query string, so no need to bother with that in the schema now
- I copied over the DSpace `search_text` field type from the DSpace Solr config (had to remove some properties so Solr would start) but it doesn’t seem to be any better at matching than the `text_en` type
- I think I need to focus on trying to return scores with conciliator

## 2018-05-16

- Discuss GDPR with James Stapleton
  - As far as I see it, we are “Data Controllers” on CGSpace because we store peoples' names, emails, and phone numbers if they register
  - We set cookies on the user’s computer, but these do not contain personally identifiable information (PII) and they are “session” cookies which are deleted when the user closes their browser
  - We use Google Analytics to track website usage, which makes Google the “Data Processor” and in this case we merely need to limit or obfuscate the information we send to them
  - As the only personally identifiable information we send is the user’s IP address, I think we only need to enable IP Address Anonymization in our `analytics.js` code snippets
  - Then we can add a “Privacy” page to CGSpace that makes all of this clear
- Silvia asked if I could sort the records in her Listings and Report output and it turns out that the options are misconfigured in `dspace/config/modules/atmire-listings-and-reports.cfg`
- I created and merged a pull request to fix the sorting issue in Listings and Reports (#374)
- Regarding the IP Address Anonymization for GDPR, I ammended the Google Analytics snippet in `page-structure-alterations.xsl` to:

    ga('send', 'pageview', {
      'anonymizeIp': true
    });

- I tested loading a certain page before and after adding this and afterwards I saw that the parameter `aip=1` was being sent with the analytics response to Google
- According to the analytics.js protocol parameter documentation this means that IPs are being anonymized
- After finding and fixing some duplicates in IITA’s `IITA_April_27` test collection on DSpace Test (10568/92703) I told Sisay that he can move them to IITA’s Journal Articles collection on CGSpace

## 2018-05-17

- Testing reconciliation of countries against Solr via conciliator, I notice that `CÔTE D'IVOIRE` doesn’t match `COTE D'IVOIRE`, whereas with reconcile-csv it does
- Also, when reconciling regions against Solr via conciliator `EASTERN AFRICA` doesn’t match `EAST AFRICA`, whereas with reconcile-csv it does
- And `SOUTH AMERICA` matches both `SOUTH ASIA` and `SOUTH AMERICA` with the same match score of 2… WTF.
- It could be that I just need to tune the query filter in Solr (currently using the example `text_en` field type)
- Oh sweet, it turns out that the issue with searching for characters with accents is called “code folding” in Solr
- You can use either a `solr.ASCIIFoldingFilterFactory` filter or a `solr.MappingCharFilterFactory` charFilter mapping against `mapping-FoldToASCII.txt`
- Also see: https://opensourceconnections.com/blog/2017/02/20/solr-utf8/
- Now `CÔTE D'IVOIRE` matches `COTE D'IVOIRE`!
- I’m not sure which method is better, perhaps the `solr.ASCIIFoldingFilterFactory` filter because it doesn’t require copying the `mapping-FoldToASCII.txt` file
- And actually I’m not entirely sure about the order of filtering before tokenizing, etc…
- Ah, I see that `charFilter` must be before the tokenizer because it works on a stream, whereas `filter` operates on tokenized input so it must come after the tokenizer
- Regarding the use of the `charFilter` vs the `filter` class before and after the tokenizer, respectively, I think it’s better to use the `charFilter` to normalize the input stream before tokenizing it as I have no idea what kinda stuff might get removed by the tokenizer
- Skype with Geoffrey from IITA in Nairobi who wants to deposit records to CGSpace via the REST API but I told him that this skips the submission workflows and because we cannot guarantee the data quality we would not allow anyone to use it this way
- I finished making the XMLUI changes for anonymization of IP addresses in Google Analytics and merged the changes to the `5_x-prod` branch (#375
- Also, I think we might be able to implement opt-out functionality for Google Analytics using a window property that could be managed by storing its status in a cookie
- This cookie could be set by a user clicking a link in a privacy policy, for example
- The additional Javascript could be easily added to our existing `googleAnalytics` template in each XMLUI theme

## 2018-05-18

- Do a final check on the thirty (30) IWMI Book Chapters for Udana and upload them to CGSpace
- These were previously on DSpace Test as “IWMI test collection” in 2018-04

## 2018-05-20

- Run all system updates on DSpace Test (linode19), re-deploy DSpace with latest `5_x-dev` branch (including GDPR IP anonymization), and reboot the server
- Run all system updates on CGSpace (linode18), re-deploy DSpace with latest `5_x-dev` branch (including GDPR IP anonymization), and reboot the server

## 2018-05-21

- Geoffrey from IITA got back with more questions about depositing items programatically into the CGSpace workflow
- I pointed out that SWORD might be an option, as DSpace supports the SWORDv2 protocol (although we have never tested it)
- Work on implementing cookie consent popup for all XMLUI themes (SASS theme with primary / secondary branding from Bootstrap)

## 2018-05-22

- Skype with James Stapleton about last minute GDPR wording
- After spending yesterday working on integration and theming of the cookieconsent popup, today I cannot get the damn “Agree” button to dismiss the popup!
- I tried calling it several ways, via jQuery, via a function in `page-structure-alterations.xsl`, via script tags in `<head>` in `page-structure.xsl`, and a few others
- The only way it actually works is if I paste it into the community or collection HTML
- Oh, actually in testing it appears this is not true
- This is a waste of TWO full days of work
- Marissa Van Epp asked if I could add `PII-FP1_PACCA2` to the CCAFS phase II project tags on CGSpace so I created a ticket to track it (#376)

## 2018-05-23

- I’m investigating how many non-CGIAR users we have registered on CGSpace:

    dspace=# select email, netid from eperson where email not like '%cgiar.org%' and email like '%@%';

- We might need to do something regarding these users for GDPR compliance because we have their names, emails, and potentially phone numbers
- I decided that I will just use the cookieconsent script as is, since it looks good and technically does set the cookie with “allow” or “dismiss”
- I wrote a quick conditional to check if the user has agreed or not before enabling Google Analytics
- I made a pull request for the GDPR compliance popup (#377) and merged it to the `5_x-prod` branch
- I will deploy it to CGSpace tonight

## 2018-05-28

- Daniel Haile-Michael sent a message that CGSpace was down (I am currently in Oregon so the time difference is ~10 hours)
- I looked in the logs but didn’t see anything that would be the cause of the crash
- Atmire finalized the DSpace 5.8 testing and sent a pull request: https://github.com/ilri/DSpace/pull/378
- They have asked if I can test this and get back to them by June 11th

## 2018-05-30

- Talk to Samantha from Bioversity about something related to Google Analytics, I’m still not sure what they want
- DSpace Test crashed last night, seems to be related to system memory (not JVM heap)
- I see this in `dmesg`:

    [Wed May 30 00:00:39 2018] Out of memory: Kill process 6082 (java) score 697 or sacrifice child
    [Wed May 30 00:00:39 2018] Killed process 6082 (java) total-vm:14876264kB, anon-rss:5683372kB, file-rss:0kB, shmem-rss:0kB
    [Wed May 30 00:00:40 2018] oom_reaper: reaped process 6082 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

- I need to check the Tomcat JVM heap size/usage, command line JVM heap size (for cron jobs), and PostgreSQL memory usage
- It might be possible to adjust some things, but eventually we’ll need a larger VPS instance
- For some reason there are no JVM stats in Munin, ugh
- Run all system updates on DSpace Test and reboot it
- I generated a list of CIFOR duplicates from the `CIFOR_May_9` collection using the Atmire MQM module and then dumped the HTML source so I could process it for sending to Vika
- I used grep to filter all relevant handle lines from the HTML source then used sed to insert a newline before each “Item1” line (as the duplicates are grouped like Item1, Item2, Item3 for each set of duplicates):

    $ grep -E 'aspect.duplicatechecker.DuplicateResults.field.del_handle_[0-9]{1,3}_Item' ~/Desktop/https\ _dspacetest.cgiar.org_atmire_metadata-quality_duplicate-checker.html > ~/cifor-duplicates.txt
    $ sed 's/.*Item1.*/\n&/g' ~/cifor-duplicates.txt > ~/cifor-duplicates-cleaned.txt

- I told Vika to look through the list manually and indicate which ones are indeed duplicates that we should delete, and which ones to map to CIFOR’s collection
- A few weeks ago Peter wanted a list of authors from the ILRI collections, so I need to find a way to get the handles of all those collections
- I can use the `/communities/{id}/collections` endpoint of the REST API but it only takes IDs (not handles) and doesn’t seem to descend into sub communities
- Shit, so I need the IDs for the the top-level ILRI community and all its sub communities (and their sub communities)
- There has got to be a better way to do this than going to each community and getting their handles and IDs manually
- Oh shit, I literally already wrote a script to get all collections in a community hierarchy from the REST API: rest-find-collections.py
- The output isn’t great, but all the handles and IDs are printed in debug mode:

    $ ./rest-find-collections.py -u https://cgspace.cgiar.org/rest -d 10568/1 2> /tmp/ilri-collections.txt

- Then I format the list of handles and put it into this SQL query to export authors from items ONLY in those collections (too many to list here):

    dspace=# \copy (select distinct text_value, count(*) from metadatavalue where metadata_field_id = (select metadata_field_id from metadatafieldregistry where element = 'contributor' and qualifier = 'author') AND resource_type_id = 2 AND resource_id IN (select item_id from collection2item where collection_id IN (select resource_id from handle where handle in ('10568/67236','10568/67274',...))) group by text_value order by count desc) to /tmp/ilri-authors.csv with csv;

## 2018-05-31

- Clarify CGSpace’s usage of Google Analytics and personally identifiable information during user registration for Bioversity team who had been asking about GDPR compliance
- Testing running PostgreSQL in a Docker container on localhost because when I’m on Arch Linux there isn’t an easily installable package for particular PostgreSQL versions
- Now I can just use Docker:

    $ docker pull postgres:9.5-alpine
    $ docker run --name dspacedb -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:9.5-alpine
    $ createuser -h localhost -U postgres --pwprompt dspacetest
    $ createdb -h localhost -U postgres -O dspacetest --encoding=UNICODE dspacetest
    $ psql -h localhost -U postgres dspacetest -c 'alter user dspacetest superuser;'
    $ pg_restore -h localhost -O -U dspacetest -d dspacetest -W -h localhost ~/Downloads/cgspace_2018-05-30.backup
    $ psql -h localhost -U postgres dspacetest -c 'alter user dspacetest nosuperuser;'
    $ psql -h localhost -U dspacetest -f ~/src/git/DSpace/dspace/etc/postgres/update-sequences.sql dspacetest
    $ psql -h localhost -U postgres dspacetest