{"action":"created","comment":{"url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/comments/219723860","pull_request_review_id":157959445,"id":219723860,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxOTcyMzg2MA==","diff_hunk":"@@ -20,6 +35,13 @@ def lxmlns(arg):\n \n \n # CONSTANTS\n-\n FILE_ID_PREFIX = 'file-'\n GROUP_ID_PREFIX = 'Group-'\n+\n+\n+# URL helpers\n+def urlencode(url):\n+    parsed = urlparse.urlparse(url)\n+    for attr in ('path', 'params', 'query', 'fragment'):\n+        parsed = parsed._replace(**{attr: quote_plus(getattr(parsed, attr), safe='/')})","path":"metsrw/utils.py","position":35,"original_position":35,"commit_id":"96ce2546af297a0311782e9e6b140b19ff62e769","original_commit_id":"96ce2546af297a0311782e9e6b140b19ff62e769","user":{"login":"sevein","id":606459,"node_id":"MDQ6VXNlcjYwNjQ1OQ==","avatar_url":"https://avatars2.githubusercontent.com/u/606459?v=4","gravatar_id":"","url":"https://api.github.com/users/sevein","html_url":"https://github.com/sevein","followers_url":"https://api.github.com/users/sevein/followers","following_url":"https://api.github.com/users/sevein/following{/other_user}","gists_url":"https://api.github.com/users/sevein/gists{/gist_id}","starred_url":"https://api.github.com/users/sevein/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sevein/subscriptions","organizations_url":"https://api.github.com/users/sevein/orgs","repos_url":"https://api.github.com/users/sevein/repos","events_url":"https://api.github.com/users/sevein/events{/privacy}","received_events_url":"https://api.github.com/users/sevein/received_events","type":"User","site_admin":false},"body":"It seems like you've been doing an effort in this PR to keep columns down to 79; FYI this one has 87.","created_at":"2018-09-24T03:09:17Z","updated_at":"2018-09-24T03:10:49Z","html_url":"https://github.com/artefactual-labs/mets-reader-writer/pull/48#discussion_r219723860","pull_request_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/comments/219723860"},"html":{"href":"https://github.com/artefactual-labs/mets-reader-writer/pull/48#discussion_r219723860"},"pull_request":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48"}}},"pull_request":{"url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48","id":217540191,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE3NTQwMTkx","html_url":"https://github.com/artefactual-labs/mets-reader-writer/pull/48","diff_url":"https://github.com/artefactual-labs/mets-reader-writer/pull/48.diff","patch_url":"https://github.com/artefactual-labs/mets-reader-writer/pull/48.patch","issue_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/48","number":48,"state":"open","locked":false,"title":"Encode XLINK HREF values using URL Encoding","user":{"login":"ross-spencer","id":1880412,"node_id":"MDQ6VXNlcjE4ODA0MTI=","avatar_url":"https://avatars1.githubusercontent.com/u/1880412?v=4","gravatar_id":"","url":"https://api.github.com/users/ross-spencer","html_url":"https://github.com/ross-spencer","followers_url":"https://api.github.com/users/ross-spencer/followers","following_url":"https://api.github.com/users/ross-spencer/following{/other_user}","gists_url":"https://api.github.com/users/ross-spencer/gists{/gist_id}","starred_url":"https://api.github.com/users/ross-spencer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ross-spencer/subscriptions","organizations_url":"https://api.github.com/users/ross-spencer/orgs","repos_url":"https://api.github.com/users/ross-spencer/repos","events_url":"https://api.github.com/users/ross-spencer/events{/privacy}","received_events_url":"https://api.github.com/users/ross-spencer/received_events","type":"User","site_admin":false},"body":"This fix resolves archivematica/issues#187 whereby an anyURI type\r\nwas not validating when it contained values that needed to be url\r\nencoded.\r\n\r\nAdditionally this commit specifies a UTF-8 encoding for the `.py`\r\nfiles in the code-base which just seems like good practice to\r\nfollow across the Archivematica repositories.\r\n\r\nConnected to archivematica/issues#187\r\n\r\n---\r\n\r\nTests have been modified to include two characters `[` and `]` that need to be url encoded. Those tests pass now as expected.\r\n\r\nFurther, this script was modified to run as `main()` **(NB.** Maybe not the best way, but the most expedient one):\r\n\r\n```\r\n#!/usr/bin/env python2\r\n# -*- coding: utf-8 -*-\r\n\r\n\"\"\"Given a transfer type Dataverse access the metadata submission object\r\n```dataset.json``` to generate a transfer METS.xml file.\r\n\r\nThe METS.xml will reflect various properties of the ```dataset.json``` file. An\r\nexample of a specific feature of Dataverse is the existence of Bundle objects\r\nfor Tabular data. Bundles contain derivatives of a tabular data file that are\r\ncreated by Dataverse to enable the data to be interacted with using as wide a\r\nrange of tools as possible. These Derivatives are transcribed to the METS.xml.\r\n\r\nMore information about Dataverse in Archivematica can be found here:\r\nhttps://wiki.archivematica.org/Dataverse\r\n\"\"\"\r\nfrom __future__ import print_function\r\nimport argparse\r\nimport json\r\nimport logging\r\nimport os\r\nimport uuid\r\n\r\nfrom lxml import etree\r\n\r\nimport metsrw\r\n\r\n\r\nLOGFORMAT = '%(asctime)-15s %(levelname)s: %(message)s'\r\nDATEFORMAT = '%m/%d/%Y %H:%M:%S'\r\nOFFLINE_ERR = (\r\n    \"The QName value '{http://www.w3.org/1999/xlink}simpleLink' does not \"\r\n    \"resolve to a(n) attribute group definition.\")\r\n\r\n\r\nclass ConvertDataverseError(Exception):\r\n    pass\r\n\r\n\r\n# Mapping from originalFormatLabel in dataset.json to file extension. The\r\n# values here are associated with Dataverse Bundles, created when Tabular data\r\n# is ingested, see: http://guides.dataverse.org/en/latest/user/dataset-\r\n# management.html?highlight=bundle\r\n# The formats supported for tabluar data ingests are here:\r\n# http://guides.dataverse.org/en/latest/user/tabulardataingest/\r\n# supportedformats.html\r\nEXTENSION_MAPPING = {\r\n    \"Comma Separated Values\": \".csv\",\r\n    \"MS Excel (XLSX)\": \".xlsx\",\r\n    \"R Data\": \".RData\",\r\n    \"SPSS Portable\": \".por\",\r\n    \"SPSS SAV\": \".sav\",\r\n    \"Stata Binary\": \".dta\",\r\n    \"Stata 13 Binary\": \".dta\",\r\n    \"UNKNOWN\": \"UNKNOWN\",\r\n}\r\n\r\n\r\ndef get_ddi_title_author(dataset_md_latest):\r\n    \"\"\"Retrieve the title and the author of the dataset for the DDI XML\r\n    snippet to be included in the METS file.\r\n    \"\"\"\r\n    title_text = author_text = None\r\n    citation = dataset_md_latest.get(\"metadataBlocks\", {}).get(\"citation\")\r\n    fields = citation.get(\"fields\", None)\r\n    if fields:\r\n        for field in fields:\r\n            if field.get(\"typeName\") == \"title\":\r\n                title_text = field.get(\"value\")\r\n            if field.get(\"typeName\") == \"author\":\r\n                author_text = field.get(\"value\")[0].get(\"authorName\")\\\r\n                    .get(\"value\")\r\n        return title_text.strip(), author_text.strip()\r\n    raise ConvertDataverseError(\r\n        \"Unable to retrieve MD fields from dataset.json\")\r\n\r\n\r\ndef create_ddi(json_metadata, dataset_md_latest):\r\n    \"\"\"Create the DDI dmdSec from the JSON metadata.\"\"\"\r\n    ddi_elems = {}\r\n\r\n    try:\r\n        ddi_elems[\"Title\"], \\\r\n            ddi_elems[\"Author\"] = get_ddi_title_author(dataset_md_latest)\r\n    except TypeError as err:\r\n        logging.error(\r\n            \"Unable to gather citation data from dataset.json: %s\", err)\r\n        return None\r\n    except ConvertDataverseError as err:\r\n        logging.error(err)\r\n        return None\r\n\r\n    ddi_elems[\"PID Type\"] = json_metadata.get(\"protocol\", \"\")\r\n    ddi_elems[\"IDNO\"] = json_metadata.get(\"persistentUrl\", \"\")\r\n    ddi_elems[\"Version Date\"] = dataset_md_latest.get(\"releaseTime\", \"\")\r\n    ddi_elems[\"Version Type\"] = dataset_md_latest.get(\"versionState\", \"\")\r\n    ddi_elems[\"Version Number\"] = \"{}.{}\".format(\r\n        dataset_md_latest.get(\"versionNumber\", \"\"),\r\n        dataset_md_latest.get(\"versionMinorNumber\", \"\")\r\n    )\r\n    ddi_elems[\"Restriction Text\"] = dataset_md_latest.get(\"termsOfUse\", \"\")\r\n    ddi_elems[\"Distributor Text\"] = json_metadata.get(\"publisher\", \"\")\r\n\r\n    draft = False\r\n    print(\"Fields retrieved from Dataverse:\")\r\n    for ddi_k, ddi_v in ddi_elems.iteritems():\r\n        if ddi_k == \"Version Type\" and ddi_v == \"DRAFT\":\r\n            draft = True\r\n        print(\"{}: {}\".format(ddi_k, ddi_v))\r\n\r\n    if draft:\r\n        print(\r\n            \"Dataset is in a DRAFT state and may not transfer correctly\")\r\n        logging.error(\r\n            \"Dataset is in a DRAFT state and may not transfer correctly\")\r\n\r\n    # Create XML.\r\n    nsmap = {\"ddi\": \"http://www.icpsr.umich.edu/DDI\"}\r\n    ddins = \"{\" + nsmap[\"ddi\"] + \"}\"\r\n    ddi_root = etree.Element(ddins + \"codebook\", nsmap=nsmap)\r\n    ddi_root.attrib[\"version\"] = \"2.5\"\r\n\r\n    root_ns = \"{http://www.w3.org/2001/XMLSchema-instance}schemaLocation\"\r\n    dv_ns = (\r\n        \"http://www.ddialliance.org/Specification/DDI-Codebook/2.5/\"\r\n        \"XMLSchema/codebook.xsd\"\r\n    )\r\n    ddi_root.attrib[root_ns] = dv_ns\r\n\r\n    stdydscr = etree.SubElement(ddi_root, ddins + \"stdyDscr\", nsmap=nsmap)\r\n    citation = etree.SubElement(stdydscr, ddins + \"citation\", nsmap=nsmap)\r\n\r\n    titlstmt = etree.SubElement(citation, ddins + \"titlStmt\", nsmap=nsmap)\r\n    etree.SubElement(titlstmt, ddins + \"titl\", nsmap=nsmap).text \\\r\n        = ddi_elems[\"Title\"]\r\n\r\n    etree.SubElement(\r\n        titlstmt, ddins + \"IDNo\", agency=ddi_elems[\"PID Type\"]).text \\\r\n        = ddi_elems[\"IDNO\"]\r\n\r\n    rspstmt = etree.SubElement(citation, ddins + \"rspStmt\")\r\n    etree.SubElement(rspstmt, ddins + \"AuthEnty\").text \\\r\n        = ddi_elems[\"Author\"]\r\n\r\n    diststmt = etree.SubElement(citation, ddins + \"distStmt\")\r\n    etree.SubElement(diststmt, ddins + \"distrbtr\").text \\\r\n        = ddi_elems[\"Distributor Text\"]\r\n\r\n    verstmt = etree.SubElement(citation, ddins + \"verStmt\")\r\n    etree.SubElement(\r\n        verstmt, ddins + \"version\", date=ddi_elems[\"Version Date\"],\r\n        type=ddi_elems[\"Version Type\"]\r\n    ).text = ddi_elems[\"Version Number\"]\r\n\r\n    dataaccs = etree.SubElement(stdydscr, ddins + \"dataAccs\")\r\n    usestmt = etree.SubElement(dataaccs, ddins + \"useStmt\")\r\n    etree.SubElement(usestmt, ddins + \"restrctn\").text \\\r\n        = ddi_elems[\"Restriction Text\"]\r\n\r\n    return ddi_root\r\n\r\n\r\ndef display_checksum_for_user(checksum_value, checksum_type=\"MD5\"):\r\n    \"\"\"Provide some feedback to the user that enables them to understand what\r\n    this script is doing in the Dataverse workflow.\r\n    \"\"\"\r\n    print(\r\n        \"Checksum retrieved from dataset.json: {} ({})\"\r\n        .format(checksum_value, checksum_type))\r\n\r\n\r\ndef create_bundle(tabfile_json):\r\n    \"\"\"Create the FSEntry objects for the various files in a Dataverse bundle\r\n    identified initially by a ```.tab``` file being requested from the\r\n    Dataverse API.\r\n\r\n    A bundle is a collection of multiple representations of a tabular data\r\n    file. Bundles are created by Dataverse to allow interaction with as wide a\r\n    range of tools as possible.\r\n\r\n    Documentation on Bundles can be found on the Dataverse pages:\r\n\r\n       * http://guides.dataverse.org/en/latest/user/dataset-management.html?\r\n       highlight=bundle\r\n    \"\"\"\r\n    # Base name is .tab with suffix stripped\r\n    tabfile_name = tabfile_json.get(\"label\")\r\n    if tabfile_name is None:\r\n        return None\r\n\r\n    # Else, continue processing.\r\n    print(\"Creating entries for tabfile bundle {}\".format(tabfile_name))\r\n    base_name = tabfile_name[:-4]\r\n    bundle = metsrw.FSEntry(path=base_name, type=\"Directory\")\r\n    # Find the original file and add it to the METS FS Entries.\r\n    tabfile_datafile = tabfile_json.get(\"dataFile\")\r\n    fname = None\r\n    ext = EXTENSION_MAPPING.get(\r\n        tabfile_datafile.get(\"originalFormatLabel\", \"\"), \"UNKNOWN\")\r\n    logging.info(\"Retrieved extension mapping value: %s\", ext)\r\n    logging.info(\r\n        \"Original file format listed as %s\",\r\n        tabfile_datafile.get(\"originalFileFormat\", \"None\"))\r\n    if ext == \"UNKNOWN\":\r\n        fname = tabfile_datafile.get(\"filename\")\r\n        logging.info(\r\n            \"Original Format Label is UNKNOWN, using filename: %s\",\r\n            fname)\r\n    if fname is None:\r\n        fname = \"{}{}\".format(base_name, ext)\r\n    checksum_value = tabfile_datafile.get(\"md5\")\r\n    if checksum_value is None:\r\n        return None\r\n    display_checksum_for_user(checksum_value)\r\n    original_file = metsrw.FSEntry(\r\n        path=\"{}/{}\".format(base_name, fname),\r\n        use=\"original\",\r\n        file_uuid=str(uuid.uuid4()),\r\n        checksumtype=\"MD5\",\r\n        checksum=checksum_value,\r\n    )\r\n    bundle.add_child(original_file)\r\n    if tabfile_datafile.get(\"originalFormatLabel\") != \"R Data\":\r\n        # RData derivative\r\n        f = metsrw.FSEntry(\r\n            path=\"{}/{}.RData\".format(base_name, base_name),\r\n            use=\"derivative\",\r\n            derived_from=original_file,\r\n            file_uuid=str(uuid.uuid4()),\r\n        )\r\n        bundle.add_child(f)\r\n\r\n    # Add expected bundle contents\r\n    # FIXME what is the actual path for the files?\r\n    # Tabfile\r\n    f = metsrw.FSEntry(\r\n        path=\"{}/{}\".format(base_name, tabfile_datafile.get(\"filename\")),\r\n        use=\"derivative\",\r\n        derived_from=original_file,\r\n        file_uuid=str(uuid.uuid4()),\r\n    )\r\n    f.add_dmdsec(\r\n        md=\"{}/{}-ddi.xml\".format(base_name, base_name),\r\n        mdtype=\"DDI\",\r\n        mode=\"mdref\",\r\n        label=\"{}-ddi.xml\".format(base_name),\r\n        loctype=\"OTHER\",\r\n        otherloctype=\"SYSTEM\",\r\n    )\r\n    bundle.add_child(f)\r\n    # -ddi.xml\r\n    f = metsrw.FSEntry(\r\n        path=\"{}/{}-ddi.xml\".format(base_name, base_name),\r\n        use=\"metadata\",\r\n        derived_from=original_file,\r\n        file_uuid=str(uuid.uuid4()),\r\n    )\r\n    bundle.add_child(f)\r\n    # citation - endnote\r\n    f = metsrw.FSEntry(\r\n        path=\"{}/{}citation-endnote.xml\".format(base_name, base_name),\r\n        use=\"metadata\",\r\n        derived_from=original_file,\r\n        file_uuid=str(uuid.uuid4()),\r\n    )\r\n    bundle.add_child(f)\r\n    # citation - ris\r\n    f = metsrw.FSEntry(\r\n        path=\"{}/{}citation-ris.ris\".format(base_name, base_name),\r\n        use=\"metadata\",\r\n        derived_from=original_file,\r\n        file_uuid=str(uuid.uuid4()),\r\n    )\r\n    bundle.add_child(f)\r\n    return bundle\r\n\r\n\r\ndef retrieve_terms_of_access(dataset_md_latest):\r\n    \"\"\"Return a tuple that can be used to direct users to information about a\r\n    dataset if it is restricted.\r\n    \"\"\"\r\n    return dataset_md_latest.get(\"termsOfAccess\")\r\n\r\n\r\ndef test_if_zip_in_name(fname):\r\n    \"\"\"Check if a file-path ends in a .zip extension. If so, return true. This\r\n    helps us to log some information about the characteristics of the package\r\n    as we go.\r\n    \"\"\"\r\n    ext_ = os.path.splitext(fname)[1]\r\n    if ext_.lower() == '.zip':\r\n        return True\r\n    return False\r\n\r\n\r\ndef add_ddi_xml(sip, json_metadata, dataset_md_latest):\r\n    \"\"\"Create a DDI XML data block and add this to the METS.\"\"\"\r\n    ddi_root = create_ddi(json_metadata, dataset_md_latest)\r\n    if ddi_root is None:\r\n        return None\r\n    sip.add_dmdsec(md=ddi_root, mdtype=\"DDI\")\r\n    return sip\r\n\r\n\r\ndef add_metadata_ref(sip, md_name, md_loc):\r\n    \"\"\"Add a single mdref to the METS file.\"\"\"\r\n    sip.add_dmdsec(\r\n        md=md_loc,\r\n        mdtype=\"OTHER\",\r\n        mode=\"mdref\",\r\n        label=md_name,\r\n        loctype=\"OTHER\",\r\n        otherloctype=\"SYSTEM\",\r\n    )\r\n    return sip\r\n\r\n\r\ndef add_md_dir_to_structmap(sip):\r\n    \"\"\"Add the metadata directory to the structmap.\"\"\"\r\n    md_dir = metsrw.FSEntry(path=\"metadata\", use=None, type=\"Directory\")\r\n    sip.add_child(md_dir)\r\n    # Add dataset.json to the fileSec output.\r\n    f = metsrw.FSEntry(\r\n        path=\"metadata/dataset.json\", use=\"metadata\",\r\n        file_uuid=str(uuid.uuid4())\r\n    )\r\n    # Add dataset.json to the metadata fileSec group.\r\n    md_dir.add_child(f)\r\n    return sip\r\n\r\n\r\ndef add_dataset_files_to_md(sip, dataset_md_latest, contact_information):\r\n    # Add original files to the METS document.\r\n    files = dataset_md_latest.get('files')\r\n    if not files:\r\n        return None\r\n\r\n    # Signal to users the existence of zip files in this transfer.\r\n    zipped_file = False\r\n\r\n    # Signal to users that this transfer might consist of metadata only.\r\n    if len(files) is 0:\r\n        logging.info(\r\n            \"Metadata only transfer? There are no file entries in this \"\r\n            \"transfer's metadata.\")\r\n\r\n    for file_json in files:\r\n        is_restricted = file_json.get(\"restricted\")\r\n        if is_restricted is True and contact_information:\r\n            logging.error(\r\n                \"Restricted dataset files may not have transferred \"\r\n                \"correctly: %s\", contact_information)\r\n\r\n        data_file = file_json.get(\"dataFile\", {})\r\n        if data_file.get(\"filename\", \"\").endswith(\".tab\"):\r\n            # A Tabular Data File from Dataverse will consist of an original\r\n            # tabular format submitted by the researcher plus multiple\r\n            # different representations. We need to map that here.\r\n            bundle = create_bundle(file_json)\r\n            if bundle:\r\n                sip.add_child(bundle)\r\n            else:\r\n                logging.error(\r\n                    \"Create Dataverse transfer METS failed. \"\r\n                    \"Bundle returned: %s\", bundle)\r\n                return None\r\n        else:\r\n            path_ = None\r\n            if data_file:\r\n                path_ = data_file.get(\"filename\")\r\n            if path_:\r\n                if test_if_zip_in_name(path_):\r\n                    # provide some additional logging around the contents of\r\n                    # the dataset we're processing.\r\n                    if not zipped_file:\r\n                        zipped_file = True\r\n                        logging.info(\r\n                            \"Non-bundle .zip file found in the dataset.\")\r\n                checksum_value = data_file.get(\"md5\")\r\n                if checksum_value is None:\r\n                    return None\r\n                display_checksum_for_user(checksum_value)\r\n                f = metsrw.FSEntry(\r\n                    path=path_,\r\n                    use=\"original\",\r\n                    file_uuid=str(uuid.uuid4()),\r\n                    checksumtype=\"MD5\",\r\n                    checksum=checksum_value,\r\n                )\r\n                sip.add_child(f)\r\n            else:\r\n                logging.error(\r\n                    \"Problem retrieving filename from metadata, returned \"\r\n                    \"datafile: %s, path: %s\", data_file, path_)\r\n                return None\r\n    return sip\r\n\r\n\r\ndef write_mets_to_file(sip, output_md_name):\r\n    metadata_name = output_md_name\r\n    if metadata_name is None:\r\n        metadata_name = \"METS_.xml\"\r\n    # Write the data structure out to a file and ensure that the encoding is\r\n    # purposely set to UTF-8. This pattern is used in ```create_mets_v2.py```.\r\n    # Given the opportunity we should add an encoding feature to the metsrw\r\n    # package.\r\n    mets_f = metsrw.METSDocument()\r\n    mets_f.append_file(sip)\r\n    with open(metadata_name, 'w') as xml_file:\r\n        xml_file.write(etree.tostring(\r\n            mets_f.serialize(), pretty_print=True, encoding=\"utf-8\",\r\n            xml_declaration=True))\r\n\r\n\r\ndef load_md_and_return_json(dataset_md_name):\r\n    # Read JSON\r\n    json_path = os.path.join(dataset_md_name)\r\n    logging.info(\"Metadata directory exists %s\", os.path.exists(json_path))\r\n    try:\r\n        with open(json_path, \"r\") as f:\r\n            return json.load(f)\r\n    except IOError as e:\r\n        logging.error(\"Error opening dataset metadata: %s\", e)\r\n        return None\r\n\r\n\r\ndef convert_dataverse_to_mets(\r\n        dataset_md_name=\"dataset.json\", output_md_path=None,\r\n        output_md_name=None):\r\n    \"\"\"Create a transfer METS file from a Dataverse's dataset.json file\"\"\"\r\n    json_metadata = load_md_and_return_json(dataset_md_name)\r\n    if json_metadata is None:\r\n        return 1\r\n    dataset_md_latest = get_latest_version_metadata(json_metadata)\r\n    if dataset_md_latest is None:\r\n        raise ConvertDataverseError(\r\n            \"Unable to find the dataset metadata section from dataset.json\")\r\n\r\n    # If a dataset is restricted we may not have access to all the files. We\r\n    # may also want to flag this dataset to the users of this service. We\r\n    # can do this here and below. We do not yet know whether this microservice\r\n    # should fail because we don't know how all datasets behave when some\r\n    # restrictions are placed on them.\r\n    contact_information = retrieve_terms_of_access(dataset_md_latest)\r\n\r\n    # Create METS\r\n    try:\r\n        sip = metsrw.FSEntry(\r\n            path=\"None\", label=get_ddi_title_author(dataset_md_latest)[0],\r\n            use=None, type=\"Directory\"\r\n        )\r\n    except TypeError as err:\r\n        citation_msg = (\r\n            \"Unable to gather citation data from dataset.json: %s\", err)\r\n        logging.error(citation_msg)\r\n        raise ConvertDataverseError(citation_msg)\r\n    except ConvertDataverseError as err:\r\n        raise\r\n\r\n    sip = add_ddi_xml(sip, json_metadata, dataset_md_latest)\r\n    if sip is None:\r\n        raise ConvertDataverseError(\"Error creating SIP from Dataverse DDI\")\r\n\r\n    sip = add_metadata_ref(\r\n        sip, dataset_md_name, \"metadata/{}\".format(dataset_md_name))\r\n\r\n    sip = add_dataset_files_to_md(sip, dataset_md_latest, contact_information)\r\n    if sip is None:\r\n        raise ConvertDataverseError(\"Error adding Dataset files to METS\")\r\n\r\n    # On success of the following two functions, the module will return None\r\n    # to JobContext which expects non-zero as a failure code only.\r\n    sip = add_md_dir_to_structmap(sip)\r\n    write_mets_to_file(sip, output_md_name)\r\n\r\n\r\ndef get_latest_version_metadata(json_metadata):\r\n    \"\"\"If the datatset has been downloaded from the Dataverse web ui then there\r\n    is a slightly different structure. While the structure is different, the\r\n    majority of fields should remain the same and work with Archivematica. Just\r\n    in case, we log the version here and inform the user of potential\r\n    compatibility issues.\r\n\r\n    Ref: https://github.com/IQSS/dataverse/issues/4715\r\n    \"\"\"\r\n    datasetVersion = json_metadata.get(\"datasetVersion\")\r\n    if datasetVersion:\r\n        logging.info(\r\n            \"Dataset seems to have been downloaded from the Dataverse Web UI.\"\r\n            \"Some features of this method may be incompatible with \"\r\n            \"Archivematica at present.\")\r\n        return datasetVersion\r\n    return json_metadata.get(\"latestVersion\")\r\n\r\n\r\ndef main():\r\n    parser = argparse.ArgumentParser(\r\n        description='convert_dv')\r\n    parser.add_argument(\r\n        'mets', metavar='M', type=str, nargs=1, default=\"dataset.json\",\r\n        help='a mets file to parse')\r\n    parser.add_argument(\r\n        '--logging', type=str, nargs=\"?\", default=\"DEBUG\",\r\n        help='logging level, INFO, DEBUG, WARNING, ERROR')\r\n\r\n    args = parser.parse_args()\r\n\r\n    if args.logging not in [\"INFO\", \"DEBUG\", \"WARNING\", \"ERROR\"]:\r\n        logging.basicConfig(\r\n            format=LOGFORMAT, datefmt=DATEFORMAT, level=\"DEBUG\")\r\n    else:\r\n        logging.basicConfig(\r\n            format=LOGFORMAT, datefmt=DATEFORMAT, level=args.logging)\r\n\r\n    try:\r\n        convert_dataverse_to_mets(\r\n            dataset_md_name=args.mets[0], output_md_path=\"\",\r\n            output_md_name=\"CONVERTED_METS.xml\")\r\n    except IndexError:\r\n        logging.error(\"No metadata file to convert\")\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n```\r\nWas used against this dataset file from the Dataverse project:\r\n```\r\n{\r\n    \"authority\": \"10.5072/FK2\",\r\n    \"id\": 880,\r\n    \"identifier\": \"QYH45Z\",\r\n    \"latestVersion\": {\r\n        \"UNF\": \"UNF:6:dLu0AaOeqoOlDbGTwUrWjA==\",\r\n        \"createTime\": \"2018-05-16T15:28:41Z\",\r\n        \"distributionDate\": \"2013-03-27\",\r\n        \"files\": [\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Publications\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"f759178d0481e04c5f8da7cab5392826\"\r\n                    },\r\n                    \"contentType\": \"application/pdf\",\r\n                    \"description\": \"Canadian Relocation Cases: Heading Towards Guidelines\",\r\n                    \"filename\": \"30_CFLQ_271_13-3-13_1524[1].pdf\",\r\n                    \"filesize\": 152948,\r\n                    \"id\": 1013,\r\n                    \"md5\": \"f759178d0481e04c5f8da7cab5392826\",\r\n                    \"originalFormatLabel\": \"UNKNOWN\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128de1-b2d9731a80e4\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"Canadian Relocation Cases: Heading Towards Guidelines\",\r\n                \"label\": \"30_CFLQ_271_13-3-13_1524[1].pdf\",\r\n                \"restricted\": false,\r\n                \"version\": 1\r\n            },\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Data\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"249fe0b2a4f60446543cfa07a33187df\"\r\n                    },\r\n                    \"contentType\": \"text/x-fixed-field\",\r\n                    \"description\": \"ASCII file\",\r\n                    \"filename\": \"relocation2011.dat\",\r\n                    \"filesize\": 137268,\r\n                    \"id\": 1014,\r\n                    \"md5\": \"249fe0b2a4f60446543cfa07a33187df\",\r\n                    \"originalFormatLabel\": \"UNKNOWN\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128df0-9772f28ac48e\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"ASCII file\",\r\n                \"label\": \"relocation2011.dat\",\r\n                \"restricted\": false,\r\n                \"version\": 1\r\n            },\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Documentation\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"fbfd3e8d1122106e3c0f8ee09afcd6fc\"\r\n                    },\r\n                    \"contentType\": \"application/x-spss-syntax\",\r\n                    \"description\": \"SPSS syntax file\",\r\n                    \"filename\": \"relocation2011.sps\",\r\n                    \"filesize\": 10258,\r\n                    \"id\": 1012,\r\n                    \"md5\": \"fbfd3e8d1122106e3c0f8ee09afcd6fc\",\r\n                    \"originalFormatLabel\": \"UNKNOWN\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128df4-9a22bfb324e0\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"SPSS syntax file\",\r\n                \"label\": \"relocation2011.sps\",\r\n                \"restricted\": false,\r\n                \"version\": 1\r\n            },\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Data\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"187999778dfe955ca8856276e7fd64a0\"\r\n                    },\r\n                    \"contentType\": \"text/tab-separated-values\",\r\n                    \"description\": \"SPSS file\",\r\n                    \"filename\": \"relocation2011.tab\",\r\n                    \"filesize\": 113109,\r\n                    \"id\": 1010,\r\n                    \"md5\": \"187999778dfe955ca8856276e7fd64a0\",\r\n                    \"originalFormatLabel\": \"UNKNOWN\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128df9-f11572473b59\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"SPSS file\",\r\n                \"label\": \"relocation2011.tab\",\r\n                \"restricted\": false,\r\n                \"version\": 1\r\n            },\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Data\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"UNF\": \"UNF:6:dLu0AaOeqoOlDbGTwUrWjA==\",\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"235c918a8eaff0f65a8044e81a5c1ca8\"\r\n                    },\r\n                    \"contentType\": \"text/tab-separated-values\",\r\n                    \"description\": \"CSV file\",\r\n                    \"filename\": \"RELOCATION_FINAL_CANADA_738TOTAL.tab\",\r\n                    \"filesize\": 306311,\r\n                    \"id\": 1011,\r\n                    \"md5\": \"235c918a8eaff0f65a8044e81a5c1ca8\",\r\n                    \"originalFileFormat\": \"text/csv\",\r\n                    \"originalFormatLabel\": \"Comma Separated Values\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128dff-7517f048c790\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"CSV file\",\r\n                \"label\": \"RELOCATION_FINAL_CANADA_738TOTAL.tab\",\r\n                \"restricted\": false,\r\n                \"version\": 2\r\n            },\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Data\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"0b62956fe7244a59c4f2358494b71da2\"\r\n                    },\r\n                    \"contentType\": \"application/vnd.ms-excel\",\r\n                    \"description\": \"Excel file\",\r\n                    \"filename\": \"RELOCATION_FINAL_CANADA_738TOTAL.xls\",\r\n                    \"filesize\": 453120,\r\n                    \"id\": 1009,\r\n                    \"md5\": \"0b62956fe7244a59c4f2358494b71da2\",\r\n                    \"originalFormatLabel\": \"UNKNOWN\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128e07-8ca0dbaec898\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"Excel file\",\r\n                \"label\": \"RELOCATION_FINAL_CANADA_738TOTAL.xls\",\r\n                \"restricted\": false,\r\n                \"version\": 1\r\n            },\r\n            {\r\n                \"categories\": [\r\n                    \"2011\",\r\n                    \"Documentation\"\r\n                ],\r\n                \"dataFile\": {\r\n                    \"checksum\": {\r\n                        \"type\": \"MD5\",\r\n                        \"value\": \"ed3bc550febecd2cbd90e056223c92c3\"\r\n                    },\r\n                    \"contentType\": \"application/pdf\",\r\n                    \"description\": \"User Guide\",\r\n                    \"filename\": \"relocation-user-guide.pdf\",\r\n                    \"filesize\": 517694,\r\n                    \"id\": 1015,\r\n                    \"md5\": \"ed3bc550febecd2cbd90e056223c92c3\",\r\n                    \"originalFormatLabel\": \"UNKNOWN\",\r\n                    \"rootDataFileId\": -1,\r\n                    \"storageIdentifier\": \"1636a128dea-966f534add97\"\r\n                },\r\n                \"datasetVersionId\": 268,\r\n                \"description\": \"User Guide\",\r\n                \"label\": \"relocation-user-guide.pdf\",\r\n                \"restricted\": false,\r\n                \"version\": 1\r\n            }\r\n        ],\r\n        \"id\": 268,\r\n        \"lastUpdateTime\": \"2018-05-16T17:53:38Z\",\r\n        \"license\": \"CC0\",\r\n        \"metadataBlocks\": {\r\n            \"citation\": {\r\n                \"displayName\": \"Citation Metadata\",\r\n                \"fields\": [\r\n                    {\r\n                        \"multiple\": false,\r\n                        \"typeClass\": \"primitive\",\r\n                        \"typeName\": \"title\",\r\n                        \"value\": \"Canadian Relocation Cases: Heading Towards Guidelines, 2011 [test]\"\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"author\",\r\n                        \"value\": [\r\n                            {\r\n                                \"authorAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorAffiliation\",\r\n                                    \"value\": \"Queen's University\"\r\n                                },\r\n                                \"authorName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorName\",\r\n                                    \"value\": \"Nicholas Bala\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"authorAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorAffiliation\",\r\n                                    \"value\": \"Queen's University\"\r\n                                },\r\n                                \"authorName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorName\",\r\n                                    \"value\": \"Andrea Wheeler\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"authorAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorAffiliation\",\r\n                                    \"value\": \"Canadian Research Institute for Law and Family\"\r\n                                },\r\n                                \"authorName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorName\",\r\n                                    \"value\": \"Joanne Paetsch\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"authorAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorAffiliation\",\r\n                                    \"value\": \"Canadian Research Institute for Law and Family\"\r\n                                },\r\n                                \"authorName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"authorName\",\r\n                                    \"value\": \"Lorne Bertrand\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"datasetContact\",\r\n                        \"value\": [\r\n                            {\r\n                                \"datasetContactAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"datasetContactAffiliation\",\r\n                                    \"value\": \"Queen's University\"\r\n                                },\r\n                                \"datasetContactEmail\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"datasetContactEmail\",\r\n                                    \"value\": \"meghan.goodchild@queensu.ca\"\r\n                                },\r\n                                \"datasetContactName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"datasetContactName\",\r\n                                    \"value\": \"Data and Government Information Centre\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"dsDescription\",\r\n                        \"value\": [\r\n                            {\r\n                                \"dsDescriptionDate\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"dsDescriptionDate\",\r\n                                    \"value\": \"2012\"\r\n                                },\r\n                                \"dsDescriptionValue\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"dsDescriptionValue\",\r\n                                    \"value\": \"Regardless of outcome, cases where a parent wants to relocate with a child following separation have a profound impact on all concerned, and are among the most difficult cases in the family justice system. While the new British Columbia Family Law Act (not yet in force) has provisions that address relocation, no other Canadian statutes address these problems, and the only relevant Supreme Court case, Gordon v Goertz, [1996] SCJ 52, offers only the most general guidance: relocation deci sions are to be based on the \\u201cbest interests of the child\\u201d. Having clearer guidance for relocation cases would be of great assistance to the courts, lawyers and families, facilitating judicial resolution, promoting settlement a nd reducing costs, but over the past 15 years the Supreme Court has repeatedly refused leave in relocation cases, and outside of B.C. no government has announced plans to address the issue.\\r\\n\\r\\nUsing Westlaw and Quicklaw, Canadian relocation cases reported in English from 2001 to the beginning of 2011 (738 in total) were identified and analyzed.\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"controlledVocabulary\",\r\n                        \"typeName\": \"subject\",\r\n                        \"value\": [\r\n                            \"Social Sciences\"\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"keyword\",\r\n                        \"value\": [\r\n                            {\r\n                                \"keywordValue\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"keywordValue\",\r\n                                    \"value\": \"Relocation\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"keywordValue\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"keywordValue\",\r\n                                    \"value\": \"Separation\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"keywordValue\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"keywordValue\",\r\n                                    \"value\": \"Family law\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"keywordValue\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"keywordValue\",\r\n                                    \"value\": \"Family court\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"keywordValue\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"keywordValue\",\r\n                                    \"value\": \"Divorce\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"publication\",\r\n                        \"value\": [\r\n                            {\r\n                                \"publicationCitation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"publicationCitation\",\r\n                                    \"value\": \"Bala, N., and Wheeler, A. (2012). Canadian Relocation Cases: Heading Towards Guidelines. Canadian Family Law Quarterly, 30(3), 271-320\"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"publicationCitation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"publicationCitation\",\r\n                                    \"value\": \"Bala, N., and Wheeler, A. (2012). More Clarity Needed for Relocation Cases. The Lawyers Weekly, February 27, 2012.\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"controlledVocabulary\",\r\n                        \"typeName\": \"language\",\r\n                        \"value\": [\r\n                            \"English\"\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"producer\",\r\n                        \"value\": [\r\n                            {\r\n                                \"producerAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"producerAffiliation\",\r\n                                    \"value\": \"Queens' University Faculty of Law\"\r\n                                },\r\n                                \"producerName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"producerName\",\r\n                                    \"value\": \"Nicholas Bala\"\r\n                                },\r\n                                \"producerURL\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"producerURL\",\r\n                                    \"value\": \"http://law.queensu.ca/ \"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": false,\r\n                        \"typeClass\": \"primitive\",\r\n                        \"typeName\": \"productionDate\",\r\n                        \"value\": \"2012\"\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"grantNumber\",\r\n                        \"value\": [\r\n                            {\r\n                                \"grantNumberAgency\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"grantNumberAgency\",\r\n                                    \"value\": \"Social Sciences and Humanities Research Council (SSHRC) \"\r\n                                }\r\n                            },\r\n                            {\r\n                                \"grantNumberAgency\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"grantNumberAgency\",\r\n                                    \"value\": \"Canadian Research Institute for Law and Family \"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"distributor\",\r\n                        \"value\": [\r\n                            {\r\n                                \"distributorAbbreviation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"distributorAbbreviation\",\r\n                                    \"value\": \"DGIC\"\r\n                                },\r\n                                \"distributorAffiliation\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"distributorAffiliation\",\r\n                                    \"value\": \"Queen's University\"\r\n                                },\r\n                                \"distributorName\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"distributorName\",\r\n                                    \"value\": \"Data and Government Information Centre\"\r\n                                },\r\n                                \"distributorURL\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"distributorURL\",\r\n                                    \"value\": \"http://library.queensu.ca/webdoc \"\r\n                                }\r\n                            }\r\n                        ]\r\n                    },\r\n                    {\r\n                        \"multiple\": false,\r\n                        \"typeClass\": \"primitive\",\r\n                        \"typeName\": \"distributionDate\",\r\n                        \"value\": \"2013-03-27\"\r\n                    },\r\n                    {\r\n                        \"multiple\": false,\r\n                        \"typeClass\": \"primitive\",\r\n                        \"typeName\": \"depositor\",\r\n                        \"value\": \"Goodchild, Meghan\"\r\n                    },\r\n                    {\r\n                        \"multiple\": false,\r\n                        \"typeClass\": \"primitive\",\r\n                        \"typeName\": \"dateOfDeposit\",\r\n                        \"value\": \"2018-05-16\"\r\n                    },\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"timePeriodCovered\",\r\n                        \"value\": [\r\n                            {\r\n                                \"timePeriodCoveredEnd\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"timePeriodCoveredEnd\",\r\n                                    \"value\": \"2011-04-30\"\r\n                                },\r\n                                \"timePeriodCoveredStart\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"primitive\",\r\n                                    \"typeName\": \"timePeriodCoveredStart\",\r\n                                    \"value\": \"2001-01-01\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    }\r\n                ]\r\n            },\r\n            \"geospatial\": {\r\n                \"displayName\": \"Geospatial Metadata\",\r\n                \"fields\": [\r\n                    {\r\n                        \"multiple\": true,\r\n                        \"typeClass\": \"compound\",\r\n                        \"typeName\": \"geographicCoverage\",\r\n                        \"value\": [\r\n                            {\r\n                                \"country\": {\r\n                                    \"multiple\": false,\r\n                                    \"typeClass\": \"controlledVocabulary\",\r\n                                    \"typeName\": \"country\",\r\n                                    \"value\": \"Canada\"\r\n                                }\r\n                            }\r\n                        ]\r\n                    }\r\n                ]\r\n            },\r\n            \"journal\": {\r\n                \"displayName\": \"Journal Metadata\",\r\n                \"fields\": []\r\n            }\r\n        },\r\n        \"productionDate\": \"Production Date\",\r\n        \"releaseTime\": \"2018-05-16T17:53:38Z\",\r\n        \"termsOfUse\": \"CC0 Waiver\",\r\n        \"versionMinorNumber\": 0,\r\n        \"versionNumber\": 1,\r\n        \"versionState\": \"RELEASED\"\r\n    },\r\n    \"persistentUrl\": \"https://doi.org/10.5072/FK2/QYH45Z\",\r\n    \"protocol\": \"doi\",\r\n    \"publicationDate\": \"2018-05-16\",\r\n    \"publisher\": \"Root Dataverse\"\r\n}\r\n```\r\nAnd the result, before and after installing this new `metsrw` package is as follows:\r\n```\r\nross-spencer@artefactual:~/Desktop/Artefactual/python-scratch/mets-valid$ python validate-mets.py CONVERTED_METS.xml \r\nValidation result. False\r\nReason: <string>:0:0:ERROR:SCHEMASV:SCHEMAV_CVC_DATATYPE_VALID_1_2_1: Element '{http://www.loc.gov/METS/}FLocat', attribute '{http://www.w3.org/1999/xlink}href': '30_CFLQ_271_13-3-13_1524[1].pdf' is not a valid value of the atomic type 'xs:anyURI'.\r\n```\r\nAfter running `python setup.py install`:\r\n```\r\nross-spencer@artefactual:~/Desktop/Artefactual/python-scratch/mets-valid$ python validate-mets.py CONVERTED_METS.xml \r\nSchema validation via XSD is valid.\r\n```","created_at":"2018-09-24T00:25:46Z","updated_at":"2018-09-24T03:10:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a7689197f8e6cd1a846a25fd77d3ae9a30fbc43b","assignee":{"login":"ross-spencer","id":1880412,"node_id":"MDQ6VXNlcjE4ODA0MTI=","avatar_url":"https://avatars1.githubusercontent.com/u/1880412?v=4","gravatar_id":"","url":"https://api.github.com/users/ross-spencer","html_url":"https://github.com/ross-spencer","followers_url":"https://api.github.com/users/ross-spencer/followers","following_url":"https://api.github.com/users/ross-spencer/following{/other_user}","gists_url":"https://api.github.com/users/ross-spencer/gists{/gist_id}","starred_url":"https://api.github.com/users/ross-spencer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ross-spencer/subscriptions","organizations_url":"https://api.github.com/users/ross-spencer/orgs","repos_url":"https://api.github.com/users/ross-spencer/repos","events_url":"https://api.github.com/users/ross-spencer/events{/privacy}","received_events_url":"https://api.github.com/users/ross-spencer/received_events","type":"User","site_admin":false},"assignees":[{"login":"ross-spencer","id":1880412,"node_id":"MDQ6VXNlcjE4ODA0MTI=","avatar_url":"https://avatars1.githubusercontent.com/u/1880412?v=4","gravatar_id":"","url":"https://api.github.com/users/ross-spencer","html_url":"https://github.com/ross-spencer","followers_url":"https://api.github.com/users/ross-spencer/followers","following_url":"https://api.github.com/users/ross-spencer/following{/other_user}","gists_url":"https://api.github.com/users/ross-spencer/gists{/gist_id}","starred_url":"https://api.github.com/users/ross-spencer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ross-spencer/subscriptions","organizations_url":"https://api.github.com/users/ross-spencer/orgs","repos_url":"https://api.github.com/users/ross-spencer/repos","events_url":"https://api.github.com/users/ross-spencer/events{/privacy}","received_events_url":"https://api.github.com/users/ross-spencer/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"jrwdunham","id":2504271,"node_id":"MDQ6VXNlcjI1MDQyNzE=","avatar_url":"https://avatars1.githubusercontent.com/u/2504271?v=4","gravatar_id":"","url":"https://api.github.com/users/jrwdunham","html_url":"https://github.com/jrwdunham","followers_url":"https://api.github.com/users/jrwdunham/followers","following_url":"https://api.github.com/users/jrwdunham/following{/other_user}","gists_url":"https://api.github.com/users/jrwdunham/gists{/gist_id}","starred_url":"https://api.github.com/users/jrwdunham/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jrwdunham/subscriptions","organizations_url":"https://api.github.com/users/jrwdunham/orgs","repos_url":"https://api.github.com/users/jrwdunham/repos","events_url":"https://api.github.com/users/jrwdunham/events{/privacy}","received_events_url":"https://api.github.com/users/jrwdunham/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48/commits","review_comments_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48/comments","review_comment_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/comments{/number}","comments_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/48/comments","statuses_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/statuses/96ce2546af297a0311782e9e6b140b19ff62e769","head":{"label":"artefactual-labs:dev/issue-187-create-valid-xlink-encoding","ref":"dev/issue-187-create-valid-xlink-encoding","sha":"96ce2546af297a0311782e9e6b140b19ff62e769","user":{"login":"artefactual-labs","id":11890453,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExODkwNDUz","avatar_url":"https://avatars0.githubusercontent.com/u/11890453?v=4","gravatar_id":"","url":"https://api.github.com/users/artefactual-labs","html_url":"https://github.com/artefactual-labs","followers_url":"https://api.github.com/users/artefactual-labs/followers","following_url":"https://api.github.com/users/artefactual-labs/following{/other_user}","gists_url":"https://api.github.com/users/artefactual-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/artefactual-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/artefactual-labs/subscriptions","organizations_url":"https://api.github.com/users/artefactual-labs/orgs","repos_url":"https://api.github.com/users/artefactual-labs/repos","events_url":"https://api.github.com/users/artefactual-labs/events{/privacy}","received_events_url":"https://api.github.com/users/artefactual-labs/received_events","type":"Organization","site_admin":false},"repo":{"id":42131415,"node_id":"MDEwOlJlcG9zaXRvcnk0MjEzMTQxNQ==","name":"mets-reader-writer","full_name":"artefactual-labs/mets-reader-writer","private":false,"owner":{"login":"artefactual-labs","id":11890453,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExODkwNDUz","avatar_url":"https://avatars0.githubusercontent.com/u/11890453?v=4","gravatar_id":"","url":"https://api.github.com/users/artefactual-labs","html_url":"https://github.com/artefactual-labs","followers_url":"https://api.github.com/users/artefactual-labs/followers","following_url":"https://api.github.com/users/artefactual-labs/following{/other_user}","gists_url":"https://api.github.com/users/artefactual-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/artefactual-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/artefactual-labs/subscriptions","organizations_url":"https://api.github.com/users/artefactual-labs/orgs","repos_url":"https://api.github.com/users/artefactual-labs/repos","events_url":"https://api.github.com/users/artefactual-labs/events{/privacy}","received_events_url":"https://api.github.com/users/artefactual-labs/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/artefactual-labs/mets-reader-writer","description":"Library to parse and create METS files, especially for Archivematica.","fork":false,"url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer","forks_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/forks","keys_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/teams","hooks_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/hooks","issue_events_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/events{/number}","events_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/events","assignees_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/assignees{/user}","branches_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/branches{/branch}","tags_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/tags","blobs_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/refs{/sha}","trees_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/statuses/{sha}","languages_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/languages","stargazers_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/stargazers","contributors_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/contributors","subscribers_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/subscribers","subscription_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/subscription","commits_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/commits{/sha}","git_commits_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/commits{/sha}","comments_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/comments{/number}","issue_comment_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/comments{/number}","contents_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/contents/{+path}","compare_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/merges","archive_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/downloads","issues_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues{/number}","pulls_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls{/number}","milestones_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/milestones{/number}","notifications_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/labels{/name}","releases_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/releases{/id}","deployments_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/deployments","created_at":"2015-09-08T18:32:19Z","updated_at":"2018-07-09T14:07:41Z","pushed_at":"2018-09-24T00:25:46Z","git_url":"git://github.com/artefactual-labs/mets-reader-writer.git","ssh_url":"git@github.com:artefactual-labs/mets-reader-writer.git","clone_url":"https://github.com/artefactual-labs/mets-reader-writer.git","svn_url":"https://github.com/artefactual-labs/mets-reader-writer","homepage":null,"size":388,"stargazers_count":10,"watchers_count":10,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":7,"mirror_url":null,"archived":false,"open_issues_count":9,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":7,"open_issues":9,"watchers":10,"default_branch":"master"}},"base":{"label":"artefactual-labs:master","ref":"master","sha":"9490d8dff42ac185998fb9a629bf08bfa7d302cb","user":{"login":"artefactual-labs","id":11890453,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExODkwNDUz","avatar_url":"https://avatars0.githubusercontent.com/u/11890453?v=4","gravatar_id":"","url":"https://api.github.com/users/artefactual-labs","html_url":"https://github.com/artefactual-labs","followers_url":"https://api.github.com/users/artefactual-labs/followers","following_url":"https://api.github.com/users/artefactual-labs/following{/other_user}","gists_url":"https://api.github.com/users/artefactual-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/artefactual-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/artefactual-labs/subscriptions","organizations_url":"https://api.github.com/users/artefactual-labs/orgs","repos_url":"https://api.github.com/users/artefactual-labs/repos","events_url":"https://api.github.com/users/artefactual-labs/events{/privacy}","received_events_url":"https://api.github.com/users/artefactual-labs/received_events","type":"Organization","site_admin":false},"repo":{"id":42131415,"node_id":"MDEwOlJlcG9zaXRvcnk0MjEzMTQxNQ==","name":"mets-reader-writer","full_name":"artefactual-labs/mets-reader-writer","private":false,"owner":{"login":"artefactual-labs","id":11890453,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExODkwNDUz","avatar_url":"https://avatars0.githubusercontent.com/u/11890453?v=4","gravatar_id":"","url":"https://api.github.com/users/artefactual-labs","html_url":"https://github.com/artefactual-labs","followers_url":"https://api.github.com/users/artefactual-labs/followers","following_url":"https://api.github.com/users/artefactual-labs/following{/other_user}","gists_url":"https://api.github.com/users/artefactual-labs/gists{/gist_id}","starred_url":"https://api.github.com/users/artefactual-labs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/artefactual-labs/subscriptions","organizations_url":"https://api.github.com/users/artefactual-labs/orgs","repos_url":"https://api.github.com/users/artefactual-labs/repos","events_url":"https://api.github.com/users/artefactual-labs/events{/privacy}","received_events_url":"https://api.github.com/users/artefactual-labs/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/artefactual-labs/mets-reader-writer","description":"Library to parse and create METS files, especially for Archivematica.","fork":false,"url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer","forks_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/forks","keys_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/teams","hooks_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/hooks","issue_events_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/events{/number}","events_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/events","assignees_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/assignees{/user}","branches_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/branches{/branch}","tags_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/tags","blobs_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/refs{/sha}","trees_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/statuses/{sha}","languages_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/languages","stargazers_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/stargazers","contributors_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/contributors","subscribers_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/subscribers","subscription_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/subscription","commits_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/commits{/sha}","git_commits_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/git/commits{/sha}","comments_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/comments{/number}","issue_comment_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/comments{/number}","contents_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/contents/{+path}","compare_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/merges","archive_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/downloads","issues_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues{/number}","pulls_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls{/number}","milestones_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/milestones{/number}","notifications_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/labels{/name}","releases_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/releases{/id}","deployments_url":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/deployments","created_at":"2015-09-08T18:32:19Z","updated_at":"2018-07-09T14:07:41Z","pushed_at":"2018-09-24T00:25:46Z","git_url":"git://github.com/artefactual-labs/mets-reader-writer.git","ssh_url":"git@github.com:artefactual-labs/mets-reader-writer.git","clone_url":"https://github.com/artefactual-labs/mets-reader-writer.git","svn_url":"https://github.com/artefactual-labs/mets-reader-writer","homepage":null,"size":388,"stargazers_count":10,"watchers_count":10,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":7,"mirror_url":null,"archived":false,"open_issues_count":9,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":7,"open_issues":9,"watchers":10,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48"},"html":{"href":"https://github.com/artefactual-labs/mets-reader-writer/pull/48"},"issue":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/48"},"comments":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/issues/48/comments"},"review_comments":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48/comments"},"review_comment":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/pulls/48/commits"},"statuses":{"href":"https://api.github.com/repos/artefactual-labs/mets-reader-writer/statuses/96ce2546af297a0311782e9e6b140b19ff62e769"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"sevein"}}