<repo_name>VTUL/VIVOHarvester<file_sep>/test/test_relationship_to_rdf.py
from unittest import TestCase

from vivotool.utils.relationxmltordf import RelationshipTranslator


class RelationshipTranslatorTestSuite(TestCase):

    # relationship document object for testing
    def setUp(self):
        self.translator = RelationshipTranslator()

        self.publication_doc = {
            "@direction": "from",
            "@category": "publication",
            "@id": 3456,
            "api:object": {
                "@id": 3456
            }
        }

        self.user_doc = {
            "@direction": "to",
            "@category": "user",
            "@id": 2345,
            "api:object": {
                "@category": "user",
                "@id": 2345,
                "@username": "username",
                "api:user-identifier-associations": {
                    "api:user-identifier-association": [
                        {
                            "@scheme": "email-address",
                            "#text": "<EMAIL>"
                        }
                    ]
                }
            }
        }

        self.doc = {
            "entry": {
                "api:relationship": {
                    "@id": 1234,
                    "api:related": [self.publication_doc, self.user_doc]
                }
            }
        }

    """Basic test cases."""

    def test_get_user_record(self):
        user_doc = self.translator.get_user_record(self.doc)
        self.assertEqual(user_doc["@category"], "user")

    def test_get_user_email(self):
        self.assertEqual(
            self.translator.get_user_email(
                self.user_doc),
            "<EMAIL>")

    def test_get_user_id(self):
        self.assertEqual(self.translator.get_user_id(self.user_doc), 2345)

    def test_get_username(self):
        self.assertEqual(
            self.translator.get_username(
                self.user_doc), "username")

    def test_get_publication_record(self):
        pub_doc = self.translator.get_publication_record(self.doc)
        self.assertEqual(pub_doc["@category"], "publication")

    def test_get_publication_id(self):
        self.assertEqual(
            self.translator.get_publication_id(
                self.publication_doc), 3456)

    def test_get_authorship_id(self):
        self.assertEqual(self.translator.get_authorship_id(self.doc), 1234)
<file_sep>/test/test_vivo.py
import requests_mock

from unittest import TestCase

from vivotool.vivo.vivo import VIVO


class TestVIVO(TestCase):

    def setUp(self):
        self.vivo = VIVO()

    def tearDown(self):
        self.vivo = None

    @requests_mock.mock()
    def test_request_vivo(self, m):
        m.post('http://foo.com/', text='vivomock')
        output = self.vivo.request_vivo(
            'http://foo.com/', "", "", "", "")
        self.assertEqual(output, None)

        data = {
            'email': '<EMAIL>',
        }

        output = self.vivo.request_vivo(
            "", "http://foo.com/", "", data, "update")
        self.assertEqual(output.status_code, 200)

    def test_get_query_content(self):

        output = self.vivo.get_query_content("", "otherop")
        self.assertEqual(output, "")

        output = self.vivo.get_query_content("test", "describe")
        expected = "DESCRIBE <test>"
        self.assertEqual(output, expected)

        output = self.vivo.get_query_content("test", "insert")
        expected = "INSERT DATA {\n" + \
            "GRAPH <http://vitro.mannlib.cornell.edu/default/vitro-kb-2> {\n" + \
            "test}\n}\n"
        self.assertEqual(output, expected)

        output = self.vivo.get_query_content("test", "delete")
        expected = "DELETE DATA {\n" + \
            "GRAPH <http://vitro.mannlib.cornell.edu/default/vitro-kb-2> {\n" + \
            "test}\n}\n"
        self.assertEqual(output, expected)
<file_sep>/vivotool/vivo/vivo.py
import requests


class VIVO:
    """docstring for VIVO"""

    def get_query_content(self, objcontent, optype):

        ops = ["describe", "insert", "delete"]
        if not optype or (optype not in ops):
            return ""

        query_content = ""

        if optype == "describe":
            query_content = "DESCRIBE <" + objcontent + ">"
            return query_content

        query_content = "DATA {\n" + \
            "GRAPH <http://vitro.mannlib.cornell.edu/default/vitro-kb-2> {\n"
        query_content += objcontent
        query_content += "}\n}\n"

        if optype == "insert":
            query_content = "INSERT " + query_content
        elif optype == "delete":
            query_content = "DELETE " + query_content

        return query_content

    def request_vivo(self, reqcontent, vivo_endpoint, headers, data, optype):

        ops = ["update", "query"]
        if not optype or (optype not in ops):
            return None

        data[optype] = reqcontent
        response = requests.post(vivo_endpoint, headers=headers, data=data)

        return response
<file_sep>/setup.py
import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="VIVOHarvester",
    version="0.2.2",
    author="<NAME>, <NAME>, <NAME>",
    author_email="<EMAIL>, <EMAIL>, <EMAIL>",
    description="VIVO harvester",
    license="MIT",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/VTUL/VIVOHarvester",
    scripts=['bin/vivotool'],
    packages=setuptools.find_packages(),
    install_requires=[
        'nose',
        'rdflib',
        'xmltodict',
        'pyyaml',
        'lxml',
        'requests',
        'cryptography',
        'pytz',
        'pymysql'
    ],
    classifiers=[
        "Programming Language :: Python :: 2.7",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
)
<file_sep>/test/test_model_publication.py
from unittest import TestCase

from vivotool.utils.models.publication_model import Publication


class TestSample(TestCase):
    def test_add_to_graph(self):
        pass
<file_sep>/vivotool/utils/relationxmltordf.py
import collections
import xmltodict

from rdflib import Graph, Literal, BNode, RDF, RDFS, URIRef, Namespace
from rdflib.namespace import FOAF, DC

from vivotool.utils.models.user_model import User
from vivotool.utils.models.publication_model import Publication, Authorship

VIVO = Namespace('http://vivoweb.org/ontology/core#')


class RelationshipTranslator(object):

    def __add_bindings(self, graph):
        graph.bind('vivo', VIVO)

    def get_user_record(self, doc):
        user_doc = None
        for related in doc['entry']['api:relationship']['api:related']:
            if related['@category'] == 'user':
                user_doc = related
        return user_doc

    def get_user_email(self, user_doc):
        user_email = None
        records = []
        user_identifier_association = user_doc['api:object'][
            'api:user-identifier-associations']['api:user-identifier-association']

        if isinstance(user_identifier_association, collections.OrderedDict):
            records.append(user_identifier_association)
        else:
            records = user_identifier_association

        email_assoc = None
        for assoc in records:
            if assoc['@scheme'] == 'email-address':
                email_assoc = assoc
        if email_assoc is not None:
            user_email = email_assoc['#text']
        return user_email

    def get_user_id(self, user_doc):
        return user_doc['api:object']['@id']

    def get_username(self, user_doc):
        return user_doc['api:object']['@username']

    def get_publication_record(self, doc):
        pub_doc = None
        for related in doc['entry']['api:relationship']['api:related']:
            if related['@category'] == 'publication':
                pub_doc = related
        return pub_doc

    def get_publication_id(self, pub_doc):
        return pub_doc['api:object']['@id']

    def get_authorship_id(self, doc):
        return doc['entry']['api:relationship']['@id']

    def parse(self, input_file, target_dir=""):
        with open(input_file) as fd:
            doc = xmltodict.parse(fd.read())
            feed = doc['feed']

        vivo_user = User()
        user_record = self.get_user_record(feed)
        if user_record is not None:
            vivo_user.user_id = self.get_user_id(user_record)
            vivo_user.username = self.get_username(user_record)
            vivo_user.email = self.get_user_email(user_record)

        vivo_publication = Publication()
        publication_record = self.get_publication_record(feed)

        if publication_record and user_record:
            vivo_publication.id = self.get_publication_id(publication_record)

            vivo_authorship = Authorship(vivo_user, vivo_publication)
            vivo_authorship.id = self.get_authorship_id(feed)

            g = Graph()
            self.__add_bindings(g)
            vivo_authorship.add_to_graph(g)
            g.serialize(target_dir + vivo_authorship.id + ".rdf", format='nt')
<file_sep>/test/test_user.py
from unittest import TestCase

from vivotool.utils.userxmltordf import UserElementXml2Rdf


class TestSample(TestCase):
    def test_convert(self):
        pass
<file_sep>/vivotool/utils/models/__init__.py
from . import publication_model
from . import user_model
<file_sep>/vivotool/utils/file_utils.py
import os
import logging
import shutil

from os import path


class Utils:
    """docstring for Utils"""

    def save_photo_file(self, content, filename):

        try:
            file = open(filename, "wb")
            for block in content.iter_content(1024):
                file.write(block)
            file.close()
        except Exception:
            logging.exception("")

    def read_file(self, filename):

        rdfcontent = ""
        try:
            with open(filename, "r") as file:
                rdfcontent = file.read()
        except Exception:
            logging.exception("")

        return rdfcontent

    def save_xml_file(self, content, filename):

        try:
            with open(filename, "w") as file:
                file.write(content)
        except Exception:
            logging.exception("")

    def listfiles(self, path, extension):

        all_files = list(
            filter(
                lambda x: x.endswith(extension),
                os.listdir(path)))

        return sorted(all_files)

    def listdeletefiles(self, path, pattern):

        all_files = list(
            filter(
                lambda x: x.startswith(pattern),
                os.listdir(path)))

        return sorted(all_files)

    def segmentlist(self, inputlist, length):

        result = []
        result = [inputlist[x:x + length]
                  for x in range(0, len(inputlist), length)]

        return result

    def mergefiles(self, folderpath, inputlist, mergefilename):

        with open(path.join(folderpath, mergefilename), 'wb') as wfd:
            for f in inputlist:
                with open(path.join(folderpath, f), 'rb') as fd:
                    shutil.copyfileobj(fd, wfd, 1024 * 1024 * 10)

        return mergefilename
<file_sep>/vivotool/utils/userxmltordf.py
import collections

from rdflib import Graph, Literal, BNode, RDF, RDFS, URIRef, Namespace
from rdflib.namespace import FOAF, DC
from datetime import datetime
# from utils.models.user_model import User, Appointment, Degree, Institution
from vivotool.utils.models.user_model import User, Appointment, Degree, Institution


class UserElementXml2Rdf(object):

    def convert(self, doc, rdf_filename):
        user = self.__get_user(doc)
        if user:
            g = Graph()
            user.add_to_graph(g)
            g.serialize(rdf_filename, format='nt')

    def __get_datetime(self, doc_date):
        yyyy = int(doc_date['api:year'])        # year is required
        # defaults to january if not defined
        mm = int(doc_date.get('api:month', 1))
        # defaults to january if not defined
        dd = int(doc_date.get('api:day', 1))
        return '{:%Y-%m-%dT%H:%M:%S}'.format(datetime(yyyy, mm, dd))

    def __get_user(self, doc):
        # TODO: handle properly when expected values don't exist...
        try:
            user = User()
            api_obj = doc['feed']['entry']['api:object']

            user.username = api_obj['@username']

            if 'api:first-name' in api_obj:
                user.first_name = api_obj['api:first-name']
            else:
                return user

            if 'api:last-name' in api_obj:
                user.last_name = api_obj['api:last-name']
            else:
                return user

            user.is_academic = api_obj.get(
                'api:is-academic', 'false').lower() == 'true'
            user.is_current_staff = api_obj.get(
                'api:is-current-staff', 'false').lower() == 'true'
            user.is_public = api_obj.get(
                'api:is-public', 'false').lower() == 'true'

            if 'api:suffix' in api_obj:
                user.suffix = api_obj['api:suffix']
            user.email = api_obj['api:email-address']

            if 'api:position' in api_obj:
                user.title = api_obj['api:position']
            user.email_is_public = api_obj.get(
                'api:institutional-email-is-public', '').lower() == 'true'

            if api_obj["api:records"]["api:record"]["api:native"]:

                userfields = api_obj["api:records"]["api:record"]["api:native"]["api:field"]

                fulldetail = False
                if (user.is_public and user.is_academic and user.is_current_staff):
                    fulldetail = True

                for f in userfields:
                    if isinstance(f, collections.OrderedDict):
                        if fulldetail and f['@name'] == 'overview':
                            if f['api:text']['@privacy'].lower() == 'public':
                                user.overview = f['api:text']['#text']
                        elif fulldetail and f['@name'] == 'academic-appointments':
                            appointments = f['api:academic-appointments']['api:academic-appointment']
                            idx = 1
                            if (isinstance(appointments, list)
                                ):  # many appointments
                                for i, appt in enumerate(
                                        f['api:academic-appointments']['api:academic-appointment']):
                                    if appt['@privacy'].lower() == 'public':
                                        appointment = self.__get_appointment(
                                            appt, user.username, idx)
                                        user.institutions.append(
                                            appointment.institution)
                                        user.appointments.append(appointment)
                                        idx += 1
                            elif isinstance(appointments, dict):  # single appointment
                                appt = appointments
                                if appt['@privacy'].lower() == 'public':
                                    appointment = self.__get_appointment(
                                        appt, user.username, idx)
                                    user.institutions.append(
                                        appointment.institution)
                                    user.appointments.append(appointment)
                        elif fulldetail and f['@name'] == 'degrees':
                            p = f['api:degrees']['api:degree']
                            if isinstance(p, collections.OrderedDict):
                                if p['@privacy'].lower() == 'public':
                                    degree = self.__get_degree(
                                        p, user.username)
                                    user.institutions.append(
                                        degree.institution)
                                    user.degrees.append(degree)
                            else:
                                for deg in f['api:degrees']['api:degree']:
                                    if deg['@privacy'].lower() == 'public':
                                        degree = self.__get_degree(
                                            deg, user.username)
                                        user.institutions.append(
                                            degree.institution)
                                        user.degrees.append(degree)
                        elif f['@name'] == 'email-addresses':
                            p = f['api:email-addresses']['api:email-address']
                            if isinstance(
                                    p, collections.OrderedDict) and p['@privacy'].lower() == 'public':
                                if ('api:type' in p) and p['api:type'].lower(
                                ) == 'work':
                                    user.email = p['api:address']
                    else:
                        if f == 'email-addresses':
                            print(
                                "This user is private and has a work email:" +
                                user.username)
                            print(f)

            # NOTE: Some information is not necessary in resulting graph
            # (e.g., Photo info, addresses, phone numbers, personal websites, Relationships)
            return user
        except KeyError as ke_error:
            # TODO: logger that doc is invalid for getting user
            print(
                'Unable able to properly parse input xml file: ' +
                str(ke_error))
            return None

    def __get_degree(self, doc, username):
        degree = Degree()
        degree.username = username
        degree.field = doc.get(
            'api:field-of-study',
            '')  # default to '' if none exists
        degree.label = '{} {}'.format(doc['api:name'], degree.field)
        degree.type = ''.join(
            x for x in doc['api:name'] if x.isalpha()).lower()
        if 'api:start-date' in doc:
            degree.start_date = self.__get_datetime(doc['api:start-date'])
        if 'api:end-date' in doc:
            degree.end_date = self.__get_datetime(doc['api:end-date'])
        degree.institution = self.__get_institution(doc['api:institution'])
        return degree

    def __get_appointment(self, doc, username, idx):
        appointment = Appointment(
            id_suffix='{}-{}'.format(username, idx), is_public=True)
        appointment.label = doc['api:position']
        if 'api:start-date' in doc:
            appointment.start_date = self.__get_datetime(doc['api:start-date'])
        if 'api:end-date' in doc:
            appointment.end_date = self.__get_datetime(doc['api:end-date'])
        appointment.institution = self.__get_institution(
            doc['api:institution'])
        return appointment

    def __get_institution(self, institution_doc):
        institution = Institution()
        for line in institution_doc['api:line']:
            if line['@type'] == 'organisation':
                institution.label = line['#text']
            elif line['@type'] == 'suborganisation':
                institution.dept = line['#text']
            elif line['@type'] == 'city':
                institution.city = line['#text']
            elif line['@type'] == 'country':
                institution.country = line['#text']
        return institution
<file_sep>/vivotool/vivo/__init__.py
from . import vivo
<file_sep>/test/test_model_user.py
from unittest import TestCase

from vivotool.utils.models.user_model import User


class TestSample(TestCase):
    def test_display_name(self):
        pass
<file_sep>/test/test_utils.py
from unittest import TestCase

from vivotool.utils.photo import Photo


class TestPhoto(TestCase):

    def test_is_photo(self):
        pass
<file_sep>/test/test_file_utils.py
import shutil
import tempfile
import os
import requests
import imghdr

from os import path
from unittest import TestCase
from vivotool.utils.file_utils import Utils


class TestFileUtils(TestCase):

    def setUp(self):
        self.utils = Utils()
        self.test_dir = tempfile.mkdtemp()

    def tearDown(self):
        self.utils = None
        shutil.rmtree(self.test_dir)

    def test_save_photo_file(self):
        testfile = path.join(self.test_dir, 'test.png')
        content = requests.get(
            "https://www.iconfinder.com/icons/216359/download/png/128")

        self.utils.save_photo_file(content, testfile)
        output = imghdr.what(testfile)
        self.assertEqual(output, "png")

    def test_read_file(self):
        testfile = path.join(self.test_dir, 'test.txt')

        with open(testfile, "w") as f:
            f.write("some content")

        output = self.utils.read_file(testfile)
        self.assertEqual(output, "some content")

    def test_save_xml_file(self):
        testfile = path.join(self.test_dir, 'test.xml')
        content = "<xml>test</xml>"

        self.utils.save_xml_file(content, testfile)

        with open(testfile, 'r') as file:
            output = file.read()
        self.assertEqual(output, content)

    def test_listfiles(self):
        xmlfile = path.join(self.test_dir, 'test.xml')
        txtfile = path.join(self.test_dir, 'test.txt')

        with open(xmlfile, 'w') as f:
            f.write("some content")

        with open(txtfile, 'w') as f:
            f.write("some content")

        expected = ['test.xml']
        output = self.utils.listfiles(self.test_dir, ".xml")
        self.assertEqual(output, expected)

    def test_listdeletefiles(self):
        deletefile = path.join(self.test_dir, 'deletetest2.xml')
        deletefile1 = path.join(self.test_dir, 'deletetest1.xml')
        txtfile = path.join(self.test_dir, 'test.txt')

        with open(deletefile, 'w') as f:
            f.write("some content")

        with open(deletefile1, 'w') as f:
            f.write("some content")

        with open(txtfile, 'w') as f:
            f.write("some content")

        expected = ['deletetest1.xml', 'deletetest2.xml']
        output = self.utils.listdeletefiles(self.test_dir, "delete")
        self.assertEqual(output, expected)

    def test_segmentlist(self):

        inputlist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        output = self.utils.segmentlist(inputlist, 3)
        expected = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

        self.assertEqual(output, expected)

        inputlist = [0]
        output = self.utils.segmentlist(inputlist, 3)
        expected = [[0]]

        self.assertEqual(output, expected)

    def test_mergefiles(self):

        inputlist = ["1.txt", "2.txt", "3.txt", "4.txt", "5.txt"]

        for x in range(1, 6):
            testfile = path.join(self.test_dir, str(x) + '.txt')
            with open(testfile, "w") as f:
                f.write("some content\n")

        testfilename = "mergefile.txt"
        mergefile = self.utils.mergefiles(
            self.test_dir, inputlist, testfilename)

        with open(path.join(self.test_dir, mergefile), "r") as file:
            output = file.read()

        self.assertEqual(
            output,
            "some content\nsome content\nsome content\nsome content\nsome content\n")

        inputlist = ["1.txt"]
        testfile = path.join(self.test_dir, '1.txt')
        with open(testfile, "w") as f:
            f.write("some content\n")

        testfilename = "mergefile.txt"
        mergefile = self.utils.mergefiles(
            self.test_dir, inputlist, testfilename)

        with open(path.join(self.test_dir, mergefile), "r") as file:
            output = file.read()

        self.assertEqual(
            output,
            "some content\n")
<file_sep>/vivotool/harvester/__init__.py
from . import elements
<file_sep>/vivotool/utils/db.py
import pymysql.cursors


class DB:
    """docstring for DB"""

    def __init__(self):
        pass

    def db_connection(self, **args):

        if "db" in args:
            conn = pymysql.connect(host=args['host'],
                                   user=args['user'],
                                   password=args['password'],
                                   db=args['db'],
                                   charset="utf8mb4",
                                   cursorclass=pymysql.cursors.DictCursor)
        else:
            conn = pymysql.connect(host=args['host'],
                                   user=args['user'],
                                   password=args['password'],
                                   charset="utf8mb4",
                                   cursorclass=pymysql.cursors.DictCursor)

        return conn

    def create_vivo_database(self, conn, dbname):
        try:

            # Create a cursor object
            cursorObject = conn.cursor()

            # Create database harvester
            createDBQuery = "CREATE Database " + dbname + ";"

            cursorObject.execute(createDBQuery)

        except Exception as e:

            print("Exeception occured:{}".format(e))

        finally:

            conn.close()

    def create_vivo_table(self, conn, tablename):
        try:

            if tablename == "users":

                createTableQuery = "CREATE TABLE `users`" \
                    "(" \
                    "`pid` varchar(20) NOT NULL," \
                    "`eid` varchar(20) NOT NULL," \
                    "`uid` varchar(20)," \
                    "`public` varchar(20)  NOT NULL DEFAULT 'N'," \
                    "`keyword` LONGTEXT," \
                    "`update_date` DATE DEFAULT NULL," \
                    "PRIMARY KEY ( pid )" \
                    ");"
            elif tablename == "publications":

                createTableQuery = "CREATE TABLE `publications` (" \
                    "`pid` varchar(20) NOT NULL," \
                    "`keyword` LONGTEXT," \
                    "`public` varchar(20) NOT NULL DEFAULT 'N'," \
                    "`update_date` DATE DEFAULT NULL," \
                    "PRIMARY KEY (`pid`)" \
                    ");"

            elif tablename == "relations":

                createTableQuery = "CREATE TABLE `relations` (" \
                    "`rid` varchar(20) NOT NULL," \
                    "`public` varchar(20) NOT NULL DEFAULT 'N'," \
                    "`update_date` DATE DEFAULT NULL," \
                    "PRIMARY KEY (`rid`)" \
                    ");"

            cursorObject = conn.cursor()
            cursorObject.execute(createTableQuery)

        except Exception as e:

            print("Exeception occured:{}".format(e))

    def execute_query(self, conn, querystring, querytype):
        try:

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)

            if querytype == "select":

                rows = cursorObject.fetchall()
                return rows

            elif querytype == "update":

                conn.commit()

        except Exception as e:

            print("Exeception occured:{}".format(e))

    def check_exist(self, conn, tablename, keyname, value):

        isExist = False

        try:
            querystring = "SELECT * from " + tablename + \
                " where " + keyname + " = \"" + value + "\";"

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            rows = cursorObject.fetchall()

            for row in rows:
                isExist = True

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return isExist

    def select_records(self, conn, tablename, keyname, value):

        try:
            querystring = "SELECT * from " + tablename + \
                " where " + keyname + " = \"" + value + "\";"

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            rows = cursorObject.fetchall()

            return rows

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return None

    def delete_record(self, conn, tablename, keyname, value):

        resp = False

        try:
            querystring = "DELETE from %s where %s = %s" % (
                tablename, keyname, value,)
            print(querystring)

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            conn.commit()

            resp = True

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return resp

    def update_user_privacy(self, conn, tablename, privacy, keyname, value):

        resp = False

        try:
            querystring = "UPDATE %s set public = \"%s\" where %s = \"%s\";" % (
                tablename, privacy, keyname, value,)
            print(querystring)
            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            conn.commit()

            resp = True

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return resp

    def insert_user(self, conn, username, elementid, uid, privacy):

        resp = False

        try:
            querystring = "INSERT INTO users (pid, eid, uid, public) VALUES (\"%s\", \"%s\", \"%s\", \"%s\");" % (
                username, elementid, uid, privacy,)

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            conn.commit()

            resp = True

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return resp

    def insert_publication(self, conn, pubid, privacy):

        resp = False

        try:

            querystring = "INSERT INTO publications (pid, public) VALUES (\"%s\", \"%s\");" % (
                pubid, privacy,)

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            conn.commit()

            resp = True

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return resp

    def insert_relation(self, conn, rid, privacy):

        resp = False

        try:

            querystring = "INSERT INTO relations (rid, public) VALUES (\"%s\", \"%s\");" % (
                rid, privacy,)

            cursorObject = conn.cursor()
            cursorObject.execute(querystring)
            conn.commit()

            resp = True

        except Exception as e:

            print("MySQL Exeception occured:{}".format(e))

        return resp
<file_sep>/bin/vivotool
#!/usr/bin/env python
# encoding=utf8
import argparse
import collections
import logging
import os
import sys
import time
import xmltodict
import yaml
import vivotool

from logging.config import fileConfig

from vivotool.utils.db import DB
from vivotool.utils.file_utils import Utils
from vivotool.utils.photo import Photo
from vivotool.utils.publicationxmltordf import PublicationXml2Rdf
from vivotool.utils.relationxmltordf import RelationshipTranslator
from vivotool.utils.userxmltordf import UserElementXml2Rdf
from vivotool.harvester.elements import Elements
from vivotool.vivo.vivo import VIVO
# from importlib import reload

reload(sys)
sys.setdefaultencoding('utf8')

parser = argparse.ArgumentParser()
parser.add_argument("--file", "-f", type=str, required=True)
parser.add_argument(
    "--optype",
    "-t",
    type=str,
    required=True,
    help="operation type")
parser.add_argument("--admin", "-a", type=str)
parser.add_argument("--db", "-b", type=str)
parser.add_argument("--day", "-d", type=int)
parser.add_argument('-o', '--outfile', help="Output file",
                    default=sys.stdout, type=argparse.FileType('w'))
args = parser.parse_args()

start_time = time.time()
config = yaml.safe_load(open(args.file))
xml_folder = config['folders']['upload']
localurl = config['folders']['localurl']
logging_config = config['logging']['file']

# DB params
host = config["db"]["host"]
port = config["db"]["port"]
user = config["db"]["user"]
password = config["db"]["password"]
database = config["db"]["database"]

# logger initialization
fileConfig(logging_config)
logger = logging.getLogger()
logger.debug('Logger initialized')


file_utils = Utils()

if args.optype == "ingest":
    logger.debug("Start ingesting......")

    vivouploads = xml_folder
    extension = '.xml'
    userxmlpath = vivouploads + "xml/users/"
    user_xml_files = file_utils.listfiles(userxmlpath, extension)
    pubxmlpath = vivouploads + "xml/publications/"
    pub_xml_files = file_utils.listfiles(pubxmlpath, extension)
    rsxmlpath = vivouploads + "xml/relations/"
    rs_xml_files = file_utils.listfiles(rsxmlpath, extension)
    userrdfpath = vivouploads + "rdf/users/"
    pubrdfpath = vivouploads + "rdf/publications/"
    rsrdfpath = vivouploads + "rdf/relations/"
    photordfpath = vivouploads + "rdf/photos/"

    uex2rdf = UserElementXml2Rdf()
    photo = Photo()

    try:
        for xmlfile in user_xml_files:
            logger.debug("Start parsing: " + userxmlpath + xmlfile)

            with open(userxmlpath + xmlfile) as fd:
                doc = xmltodict.parse(fd.read())

            userfilename = "rdf/users/" + xmlfile.replace(".xml", ".rdf")
            uex2rdf.convert(doc, vivouploads + userfilename)

            api_obj = doc['feed']['entry']['api:object']
            pid = api_obj['@username']
            eid = api_obj["@id"]
            public = api_obj["api:is-public"]

            if public == 'true':
                photofilename = "rdf/photos/" + eid + ".rdf"
                file_utils.save_xml_file(
                    photo.create_user_photo_graph(
                        pid, eid), vivouploads + photofilename)
            os.remove(userxmlpath + xmlfile)

    except IOError:
        logger.error('Error parsing file:' + xmlfile)

    pub_xml_to_rdf = PublicationXml2Rdf()

    try:
        for xmlfile in pub_xml_files:
            logger.debug("Start parsing: " + pubxmlpath + xmlfile)

            pub_xml_to_rdf.parse(
                pubxmlpath + xmlfile,
                pubrdfpath)
            os.remove(pubxmlpath + xmlfile)

    except IOError:
        logger.error('Error parsing file:' + xmlfile)

    translator = RelationshipTranslator()

    try:
        for xmlfile in rs_xml_files:
            logger.debug("Start parsing: " + rsxmlpath + xmlfile)

            translator.parse(
                rsxmlpath +
                xmlfile,
                rsrdfpath)
            os.remove(rsxmlpath + xmlfile)

    except IOError:
        logger.error('Error parsing file:' + xmlfile)

    # ingest to VIVO

    extension = '.rdf'
    user_rdf_files = file_utils.listfiles(userrdfpath, extension)
    photo_rdf_files = file_utils.listfiles(photordfpath, extension)
    pub_rdf_files = file_utils.listfiles(pubrdfpath, extension)
    rs_rdf_files = file_utils.listfiles(rsrdfpath, extension)

    vivousername = config['vivo']['username']
    vivopassword = config['vivo']['password']
    vivo_endpoint = config['vivo']['url']
    rdfurl = config['folders']['localurl']

    headers = {
        'Accept': "text/turtle",
    }

    data = {
        'email': vivousername,
        'password': <PASSWORD>
    }

    vivo_endpoint = vivo_endpoint + '/api/sparqlUpdate'

    if not (args.admin == "stopingest"):
        vivo = VIVO()

        for rdf_file in user_rdf_files:
            objectns = file_utils.read_file(userrdfpath + rdf_file)
            logger.debug("Process user rdf file: " + rdf_file)
            reqcontent = vivo.get_query_content(objectns, "insert")
            response = vivo.request_vivo(
                reqcontent, vivo_endpoint, headers, data, "update")
            logger.debug(response.text)
            os.remove(userrdfpath + rdf_file)
        for rdf_file in photo_rdf_files:
            objectns = file_utils.read_file(photordfpath + rdf_file)
            logger.debug("Process photo rdf file: " + rdf_file)
            reqcontent = vivo.get_query_content(objectns, "insert")
            response = vivo.request_vivo(
                reqcontent, vivo_endpoint, headers, data, "update")
            logger.debug(response.text)
            os.remove(photordfpath + rdf_file)
        for rdf_file in pub_rdf_files:
            objectns = file_utils.read_file(pubrdfpath + rdf_file)
            logger.debug("Process publication rdf file: " + rdf_file)
            reqcontent = vivo.get_query_content(objectns, "insert")
            response = vivo.request_vivo(
                reqcontent, vivo_endpoint, headers, data, "update")
            logger.debug(response.text)
            os.remove(pubrdfpath + rdf_file)
        for rdf_file in rs_rdf_files:
            objectns = file_utils.read_file(rsrdfpath + rdf_file)
            logger.debug("Process relations rdf file: " + rdf_file)
            reqcontent = vivo.get_query_content(objectns, "insert")
            response = vivo.request_vivo(
                reqcontent, vivo_endpoint, headers, data, "update")
            logger.debug(response.text)
            os.remove(rsrdfpath + rdf_file)

elif args.optype == "harvest":
    logger.debug("Start harvesting......")

    elements_endpoint = config['elements']['url']
    authorization = config['elements']['authorization']
    image_folder = config['folders']['photo']

    headers = {}
    headers["Authorization"] = "Basic " + authorization

    logger.debug('Stage1: Harvesting all elements users')

    xmlfilename = xml_folder + "xml/temp/alluser.xml"

    elements = Elements()

    if args.day and args.day > 0:
        day = args.day
    else:
        day = 0

    logger.debug('Harvesting from Elements the dey before %s day', str(day))

    elements.harvest_elements_xml(
        elements_endpoint,
        headers,
        "users",
        25,
        xmlfilename,
        day)

    logger.debug('Harvesting individual user from Elements.')

    total_public = 0
    total_academic = 0
    total_current_staff = 0

    if not (args.db == "off"):
        db = DB()

        dbconn = db.db_connection(
            host=host,
            user=user,
            password=<PASSWORD>,
            db=database)

    extension = '.xml'

    all_user_folder = xml_folder + "xml/temp/"
    all_xml_files = file_utils.listfiles(all_user_folder, extension)

    vivousername = config['vivo']['username']
    vivopassword = config['vivo']['password']
    vivo_endpoint = config['vivo']['url']
    rdfurl = config['folders']['localurl']

    vivoheaders = {
        'Accept': "text/turtle",
    }

    data = {
        'email': vivousername,
        'password': <PASSWORD>
    }

    vivo_query_endpoint = vivo_endpoint + '/api/sparqlQuery'
    vivo_update_endpoint = vivo_endpoint + '/api/sparqlUpdate'
    vivo_namespace = "http://collab.vt.edu/vivo/individual/"
    vivo = VIVO()

    # UC1: public user change the publication privacy settings
    if args.day and args.day > 0:
        logger.debug("Checking public user's publications from the giving day")

        if not (args.db == "off"):
            rows = db.select_records(dbconn, "users", "public", "Y")

            for row in rows:
                xmlfilename = xml_folder + "xml/temp/" + \
                    row['eid'].strip() + "daypublications.xml"
                elements.harvest_elements_xml(
                    elements_endpoint,
                    headers,
                    "publications",
                    row['eid'].strip(),
                    xmlfilename, day)

    for xmlfile in all_xml_files:
        with open(all_user_folder + xmlfile) as fd:
            doc = xmltodict.parse(fd.read())

        if "entry" in doc["feed"]:
            entries = doc["feed"]["entry"]
            for e in entries:
                elementid = e["api:object"]["@id"]
                username = e["api:object"]["@username"]
                if "@proprietary-id" in e["api:object"]:
                    uid = e["api:object"]["@proprietary-id"]
                else:
                    uid = ""
                is_public = e["api:object"]["api:is-public"]
                is_academic = e["api:object"]["api:is-academic"]
                is_current_staff = e["api:object"]["api:is-current-staff"]

                if is_public == "true":
                    total_public += 1

                if is_academic == "true":
                    total_academic += 1

                if is_current_staff == "true":
                    total_current_staff += 1

                if args.admin == "publicall":
                    is_public = "true"

                isExist = False
                if not (args.db == "off"):
                    isExist = db.check_exist(dbconn, "users", "pid", username)

                if isExist:
                    logger.debug(
                        "Delete user XML file:" +
                        all_user_folder +
                        xmlfile)
                    # remove user from VIVO for new insert/update or delete
                    uservivourl = vivo_namespace + username
                    logger.debug(uservivourl)
                    reqcontent = vivo.get_query_content(
                        uservivourl, "describe")
                    response = vivo.request_vivo(
                        reqcontent, vivo_query_endpoint, vivoheaders, data, "query")
                    rdfcontent = response.text
                    logger.debug(rdfcontent)
                    reqcontent = vivo.get_query_content(rdfcontent, "delete")
                    response = vivo.request_vivo(
                        reqcontent, vivo_update_endpoint, vivoheaders, data, "update")
                    logger.debug(
                        "Response from VIVO: %s for deleting %s",
                        response.text,
                        uservivourl)
                    logger.debug("The deleted Elements user id: " + elementid)
                    # fetch the publications xml from elements and mark as
                    # delete
                    xmlfilename = xml_folder + "xml/temp/delete" + elementid + "publications.xml"
                    elements.harvest_elements_xml(
                        elements_endpoint,
                        headers,
                        "publications",
                        elementid,
                        xmlfilename)

                db_op = "none"
                if is_public == "true" and is_academic == "true" and is_current_staff == "true":

                    logger.debug('Harvesting Elements public user id: ' + elementid)

                    filename = xml_folder + "xml/users/" + elementid + ".xml"
                    elements.harvest_elements_xml(
                        elements_endpoint, headers, "user", elementid, filename)

                    logger.debug(
                        'Harvesting Elements user id: ' +
                        elementid +
                        '\'s publications')

                    xmlfilename = xml_folder + "xml/temp/" + elementid + "publications.xml"
                    elements.harvest_elements_xml(
                        elements_endpoint,
                        headers,
                        "publications",
                        elementid,
                        xmlfilename)

                    logger.debug(
                        'Harvesting Elements user id: ' +
                        elementid +
                        '\'s photo')

                    elements.harvest_elements_xml(
                        elements_endpoint, headers, "photo", elementid, image_folder)

                    db_op = "topublic" if isExist else "public"

                elif is_public == "false" and is_academic == "true" and is_current_staff == "true":

                    if not (
                            args.admin == "publiconly" or args.admin == "publicall"):

                        db_op = "toprivate" if isExist else "private"

                        logger.debug(
                            'Harvesting Elements private user id: ' + elementid)

                        filename = xml_folder + "xml/users/" + elementid + ".xml"
                        elements.harvest_elements_xml(
                            elements_endpoint, headers, "user", elementid, filename)

                elif is_current_staff == "false":

                    if isExist:
                        db_op = "delete"

                if not (args.db == "off"):
                    if db_op == "delete":
                        db.delete_record(dbconn, "users", "pid", username)
                    elif db_op == "toprivate":
                        db.update_user_privacy(
                            dbconn, "users", "N", "pid", username)
                    elif db_op == "private":
                        db.insert_user(
                            dbconn, username, str(elementid), str(uid), "N")
                    elif db_op == "topublic":
                        db.update_user_privacy(
                            dbconn, "users", "Y", "pid", username)
                    elif db_op == "public":
                        db.insert_user(
                            dbconn, username, str(elementid), str(uid), "Y")

        os.remove(all_user_folder + xmlfile)

    pubfolder = xml_folder + "xml/temp/"

    eid = ""
    pubid = ""
    # handle delete publication file first
    logger.debug("Start deleting user's publications for update.")
    delfiles = file_utils.listdeletefiles(pubfolder, "delete")
    for delfile in delfiles:
        with open(pubfolder + delfile) as fd:
            doc = xmltodict.parse(fd.read())

        if "entry" in doc["feed"]:
            recid = doc["feed"]["id"]
            eid = recid.split("/")[-2]
            entries = doc["feed"]["entry"]
            logger.debug("Process publication deletion file: " + delfile)
            records = []

            if isinstance(entries, collections.OrderedDict):
                records.append(entries)
            else:
                records = entries

            for e in records:
                pubid = e["api:relationship"]["api:related"]["@id"]

                logger.debug(
                    "Deleting publication id:" + pubid)
                # remove it from VIVO for new insert or delete
                pubvivourl = vivo_namespace + "publication" + pubid
                logger.debug(pubvivourl)
                reqcontent = vivo.get_query_content(pubvivourl, "describe")
                response = vivo.request_vivo(
                    reqcontent, vivo_query_endpoint, vivoheaders, data, "query")
                rdfcontent = response.text
                # logger.debug(rdfcontent)
                reqcontent = vivo.get_query_content(rdfcontent, "delete")
                response = vivo.request_vivo(
                    reqcontent, vivo_update_endpoint, vivoheaders, data, "update")
                logger.debug(
                    "Delete response from VIVO: %s for deleting %s",
                    str(response.status_code),
                    pubvivourl)
                # fetch the relationship xml from elements and mark as delete
                logger.debug("Harvesting relationships for deletion......")
                filename = xml_folder + "xml/temp/delete" + pubid + "relationships.xml"
                elements.harvest_elements_xml(
                    elements_endpoint, headers, "pubrelationships", pubid,
                    filename)

                if not (args.db == "off"):
                    db.delete_record(dbconn, "publications", "pid", str(pubid))

        os.remove(pubfolder + delfile)

    xmlfiles = file_utils.listfiles(pubfolder, extension)

    eid = ""
    pubid = ""

    logger.debug("Start processing individual publication file.")

    for xmlfile in xmlfiles:
        # There are two "delete" files in the folder, publication and
        # relationships
        if "delete" not in xmlfile:
            logger.debug("Start processing publication files in the temp folder: " + xmlfile)
            with open(pubfolder + xmlfile) as fd:
                doc = xmltodict.parse(fd.read())

            if "entry" in doc["feed"]:
                recid = doc["feed"]["id"]
                eid = recid.split("/")[-2]
                entries = doc["feed"]["entry"]
                logger.debug("Processing all the publications in file: " + xmlfile)
                records = []

                if isinstance(entries, collections.OrderedDict):
                    records.append(entries)
                else:
                    records = entries

                for e in records:

                    authorship = e["api:relationship"]["@type"]
                    pubid = e["api:relationship"]["api:related"]["@id"]
                    logger.debug("Parsing publication id: " + pubid)
                    logger.debug("authorship type: " + authorship)

                    # check if rec is already in db
                    isExist = False
                    if not (args.db == "off"):
                        isExist = db.check_exist(
                            dbconn, "publications", "pid", str(pubid))

                    if isExist and authorship == "publication-user-authorship":
                        logger.debug(
                            "Deleting a publication file:" + pubfolder + xmlfile)
                        # remove it from VIVO for new insert or delete
                        pubvivourl = vivo_namespace + "publication" + pubid
                        # logger.debug(pubvivourl)
                        reqcontent = vivo.get_query_content(
                            pubvivourl, "describe")
                        response = vivo.request_vivo(
                            reqcontent, vivo_query_endpoint, vivoheaders, data, "query")
                        rdfcontent = response.text
                        # logger.debug(rdfcontent)
                        reqcontent = vivo.get_query_content(
                            rdfcontent, "delete")
                        response = vivo.request_vivo(
                            reqcontent, vivo_update_endpoint, vivoheaders, data, "update")
                        logger.debug(
                            "Response from VIVO: %s for deleting publication id: %s",
                            response.text,
                            pubid)
                        # fetch the relationship xml from elements and mark as
                        # delete
                        filename = xml_folder + "xml/temp/delete" + pubid + "relationships.xml"
                        elements.harvest_elements_xml(
                            elements_endpoint, headers, "pubrelationships", pubid, filename)

                    if "api:is-visible" in e["api:relationship"] and authorship == "publication-user-authorship":

                        visible = e["api:relationship"]["api:is-visible"]
                        public = e["api:relationship"]["api:related"]["api:object"]["api:is-public"]

                        if args.admin == "publicall":
                            public = "true"
                            visible = "true"

                        db_op = "none"
                        if public == "true" and visible == "true":
                            logger.debug(
                                'Harvesting Elements user id: ' +
                                eid +
                                '\'s publication id: ' +
                                pubid)

                            filename = xml_folder + "xml/publications/" + eid + "-" + pubid + ".xml"
                            elements.harvest_elements_xml(
                                elements_endpoint, headers, "publication", pubid, filename)

                            if not isExist:
                                db_op = "insert"

                            logger.debug(
                                'Harvesting publication id: ' +
                                pubid +
                                '\'s relationships')

                            filename = xml_folder + "xml/temp/" + pubid + "relationships.xml"
                            elements.harvest_elements_xml(
                                elements_endpoint, headers, "pubrelationships", pubid, filename)
                        else:

                            if isExist:
                                db_op = "delete"

                        if not (args.db == "off"):

                            if db_op == "insert":
                                db.insert_publication(dbconn, pubid, "Y")
                            elif db_op == "delete":
                                db.delete_record(
                                    dbconn, "publications", "pid", str(pubid))

                    else:
                        logger.debug(
                            'This publication has no visible attr or not publication-user-authorship')

            os.remove(pubfolder + xmlfile)

    # Process all relationships
    logger.debug('Start processing individual relationship......')

    rsfolder = xml_folder + "xml/temp/"

    # list files contain delete and delete first
    logger.debug("Handing relastions deletion files......")
    delfiles = file_utils.listdeletefiles(rsfolder, "delete")
    for delfile in delfiles:
        with open(rsfolder + delfile) as fd:
            doc = xmltodict.parse(fd.read())

        recid = doc["feed"]["id"]
        pubid = recid.split("/")[-2]

        if "entry" in doc["feed"]:
            entries = doc["feed"]["entry"]

            logger.debug(
                'Processing relationships file to be deleted in xml/temp - ' +
                delfile)

            records = []
            if isinstance(entries, collections.OrderedDict):
                records.append(entries)
            else:
                records = entries

            for e in records:

                if "api:relationship" in e:
                    rsid = e["api:relationship"]["@id"]
                    rstype = e["api:relationship"]["@type"]

                    for rec in e["link"]:
                        if "users" in rec["@href"]:
                            eid = rec["@href"].split("/")[-1]

                    logger.debug(
                        'Deleting relationship record - rsid: %s, pubid: %s, eid: %s',
                        rsid,
                        pubid,
                        eid)

                    authorship = vivo_namespace + "authorship" + pubid + "-" + eid

                    # remove it from VIVO
                    reqcontent = vivo.get_query_content(authorship, "describe")
                    response = vivo.request_vivo(
                        reqcontent, vivo_query_endpoint, vivoheaders, data, "query")
                    rdfcontent = response.text
                    reqcontent = vivo.get_query_content(rdfcontent, "delete")
                    response = vivo.request_vivo(
                        reqcontent, vivo_update_endpoint, vivoheaders, data, "update")
                    logger.debug(
                        "Response from VIVO: %s for deleting %s",
                        str(response.status_code),
                        authorship)

                    if not (args.db == "off"):
                        db.delete_record(dbconn, "relations", "rid", str(rsid))

        os.remove(rsfolder + delfile)

    xmlfiles = file_utils.listfiles(rsfolder, extension)
    logger.debug(xmlfiles)

    logger.debug("Start processing relastions files......")
    for xmlfile in xmlfiles:
        with open(rsfolder + xmlfile) as fd:
            doc = xmltodict.parse(fd.read())

        recid = doc["feed"]["id"]
        pubid = recid.split("/")[-2]

        if "entry" in doc["feed"]:
            entries = doc["feed"]["entry"]

            logger.debug('Parsing relationship file: ' + xmlfile)

            records = []
            if isinstance(entries, collections.OrderedDict):
                records.append(entries)
            else:
                records = entries

            for e in records:

                if "api:relationship" in e:
                    rsid = e["api:relationship"]["@id"]
                    rstype = e["api:relationship"]["@type"]

                    for rec in e["link"]:
                        if "users" in rec["@href"]:
                            eid = rec["@href"].split("/")[-1]

                    logger.debug(
                        'Process rsid: %s, pubid: %s, eid: %s',
                        rsid,
                        pubid,
                        eid)

                    if rstype == "publication-user-authorship":
                        visible = e["api:relationship"]["api:is-visible"]
                        public = e["api:relationship"]["api:related"]["api:object"]["api:is-public"]
                    else:
                        visible = "false"

                    if args.admin == "publicall":
                        visible = "true"
                        public = "true"

                    # check if rec is already in db
                    isExist = False
                    if not (args.db == "off"):
                        isExist = db.check_exist(
                            dbconn, "relations", "rid", str(rsid))

                    authorship = vivo_namespace + "authorship" + pubid + "-" + eid

                    if isExist:
                        # remove it from VIVO for new insert or delete anyway
                        reqcontent = vivo.get_query_content(
                            authorship, "describe")
                        response = vivo.request_vivo(
                            reqcontent, vivo_query_endpoint, vivoheaders, data, "query")
                        rdfcontent = response.text
                        reqcontent = vivo.get_query_content(
                            rdfcontent, "delete")
                        response = vivo.request_vivo(
                            reqcontent, vivo_update_endpoint, vivoheaders, data, "update")
                        logger.debug(
                            "Delete response from VIVO: %s for deleting %s",
                            str(response.status_code),
                            authorship)

                    db_op = "none"
                    if visible == "true" and public == "true":

                        logger.debug(
                            'Harvesting Elements relationship id: ' + rsid)

                        filename = xml_folder + "xml/relations/" + pubid + "-" + rsid + ".xml"
                        elements.harvest_elements_xml(
                            elements_endpoint, headers, "relationship", rsid, filename)

                        if not isExist:
                            db_op = "insert"

                    else:

                        if isExist:
                            db_op = "delete"

                    if not (args.db == "off"):

                        if db_op == "insert":
                            db.insert_relation(dbconn, str(rsid), "Y")
                        elif db_op == "delete":
                            db.delete_record(
                                dbconn, "relations", "rid", str(rsid))

        os.remove(rsfolder + xmlfile)

    if not (args.db == "off"):
        dbconn.close()

    logger.debug("Total public: " + str(total_public))
    logger.debug("Total academic: " + str(total_academic))
    logger.debug("Total current staff: " + str(total_current_staff))


elif args.optype == "db":
    logger.debug("Database operation")

    # create database
    db = DB()
    dbconn = db.db_connection(host=host, user=user, password=<PASSWORD>)
    db.create_vivo_database(dbconn, database)
    logger.debug("Database %s is created", database)

    dbconn = db.db_connection(
        host=host,
        user=user,
        password=<PASSWORD>,
        db=database)

    try:

        db.create_vivo_table(dbconn, "users")
        db.create_vivo_table(dbconn, "publications")
        db.create_vivo_table(dbconn, "relations")

        sqlQuery = "show tables;"
        rows = db.execute_query(dbconn, sqlQuery, "select")

        for row in rows:
            logger.debug("%s is created", row)

    except Exception as e:

        logger.debug("Exeception occured:{}".format(e))

    finally:

        dbconn.close()

elif args.optype == "getuser":

    db = DB()

    dbconn = db.db_connection(
        host=host,
        user=user,
        password=<PASSWORD>,
        db=database)

    rows = db.select_records(dbconn, "users", "public", "Y")

    content = "UID,PID\n"
    for row in rows:
        content = content + row['uid'].strip() + \
            "," + row['pid'].strip() + "\n"

    if args.outfile.name != "<stdout>":
        args.outfile.write(content)
        args.outfile.close()
    else:
        file_utils.save_xml_file(content, "user_map.csv")

    dbconn.close()

else:
    logger.debug("Wrong operation type")

logger.debug("--- %s seconds ---" % (time.time() - start_time))
<file_sep>/vivotool/utils/models/publication_model.py
from rdflib import BNode, Namespace, Literal, URIRef, RDF, RDFS, XSD
from datetime import datetime
import pytz
import re

COLLAB_VT = Namespace("http://collab.vt.edu/vivo/individual/")
BIBO = Namespace("http://purl.org/ontology/bibo/")
VCARD = Namespace('http://www.w3.org/2006/vcard/ns#')
VIVO = Namespace('http://vivoweb.org/ontology/core#')
OBO = Namespace('http://purl.obolibrary.org/obo/')
SKOS = Namespace('http://www.w3.org/2008/05/skos#')


class Publication:
    def __init__(self):
        self.id = ''
        self.is_public = False
        self.is_publication = True
        self.types = list()
        self.title = None
        self.abstract = None
        self.issue = None
        self.handle = None
        self.doi = None
        self.pm_id = None
        self.page_start = None
        self.page_end = None
        self.volume = None
        self.keywords = list()
        self.subject_areas = list()
        self.status = None
        self.publication_date = None
        self.journal = None
        self.webpages_publisher = None
        self.authorships = list()

    def __add_bindings(self, graph):
        graph.bind('bibo', BIBO)
        graph.bind('vcard', VCARD)
        graph.bind('vivo', VIVO)
        graph.bind('obo', OBO)
        graph.bind('skos', SKOS)

    def add_to_graph(self, graph):
        self.__add_bindings(graph)

        this = COLLAB_VT['publication' + self.id]
        # types
        for pub_type in self.types:
            if pub_type in [
                'AcademicArticle',
                'Book',
                'Chapter',
                'Journal',
                'Patent',
                'Report',
                    'Thesis']:
                graph.add((this, RDF.type, BIBO[pub_type]))
            elif pub_type == 'Software':
                graph.add((this, RDF.type, OBO['ERO_0000071']))
            else:
                graph.add((this, RDF.type, VIVO[pub_type]))
        graph.add((this, RDFS.label, Literal(self.title)))
        if self.abstract:
            graph.add((this, BIBO.abstract, Literal(self.abstract)))
        if self.issue:
            graph.add((this, BIBO.issue, Literal(self.issue)))
        if self.doi:
            graph.add((this, BIBO.doi, Literal(self.doi)))
        if self.pm_id:
            graph.add((this, BIBO.pmid, Literal(self.pm_id)))
        if self.handle:
            graph.add((this, BIBO.handle, Literal(self.handle)))
        if self.page_start:
            graph.add((this, BIBO.pageStart, Literal(self.page_start)))
        if self.page_start:
            graph.add((this, BIBO.pageEnd, Literal(self.page_end)))
        if self.volume:
            graph.add((this, BIBO.volume, Literal(self.volume)))

        # keywords
        for k in self.keywords:
            graph.add((this, VIVO.freetextKeyword, Literal(k)))

        # subject areas
        for sub_area in self.subject_areas:
            sub_area.uri = COLLAB_VT[sub_area.name()]
            sub_area.add_to_graph(graph, this)
            graph.add((this, VIVO.hasSubjectArea, sub_area.uri))

        # publication date
        if self.publication_date:
            publication_date = self.publication_date
            publication_date.uri = URIRef(this + '-publicationDate')
            publication_date.add_to_graph(graph)
            graph.add((this, VIVO.dateTimeValue, publication_date.uri))

        # journal
        if self.journal:
            journal = self.journal
            j_label = re.sub(r'["<>#%\{\}\|\\\^~\[\]`]', '', journal.label)
            journal.uri = COLLAB_VT['journal-' +
                                    j_label.lower().replace(' ', '-')]
            journal.add_to_graph(graph, this)
            graph.add((this, VIVO.hasPublicationVenue, journal.uri))

        # webpages and webpages publisher
        if self.webpages_publisher:
            webpages_publisher = self.webpages_publisher
            webpages_publisher.add_to_graph(graph, this)

        # published or not status
        if self.status:
            graph.add(
                (this, BIBO.status, BIBO[self.status.lower().replace(' ', '-')]))

        # authorships
        for authorship in self.authorships:
            authorship.add_to_graph(graph)


class PublicationDate:
    def __init__(self, id=None, month=1, day=1):
        self.id = id
        self.time_precision = None
        self.year = None
        self.month = month
        self.day = day
        self.type = None

    @staticmethod
    def format_datetime(yyyy, mm, dd):
        d = datetime(yyyy, mm, dd, tzinfo=pytz.utc)
        return str(d.isoformat()).replace('+00:00', 'Z')

    def add_to_graph(self, graph):
        this = self.uri
        graph.add((this, VIVO.dateTimePrecision, VIVO[self.time_precision]))
        if self.year:
            graph.add(
                (this, VIVO.dateTime, Literal(
                    PublicationDate.format_datetime(
                        self.year, self.month, self.day))))
        graph.add((this, RDF.type, VIVO.DateTimeValue))


class Journal:
    def __init__(self, type='Journal'):
        self.label = None
        self.type = type
        self.issn = None
        self.eissn = None
        self.subject_areas = list()

    def add_to_graph(self, graph, venue_for):
        this = self.uri
        graph.add((this, RDFS.label, Literal(self.label)))
        graph.add((this, RDF.type, BIBO[self.type]))
        graph.add((this, VIVO.publicationVenueFor, venue_for))
        if self.issn:
            graph.add((this, BIBO.issn, Literal(self.issn)))
        if self.eissn:
            graph.add((this, BIBO.eissn, Literal(self.eissn)))
        # subject areas
        for sub_area in self.subject_areas:
            sub_area.uri = COLLAB_VT[sub_area.name()]
            sub_area.add_to_graph(graph, this)
            graph.add((this, VIVO.hasSubjectArea, sub_area.uri))


class WebpagesPublisher:
    def __init__(self, type="publisher"):
        self.type = type
        self.label = None
        self.url = None

    def add_to_graph(self, graph, webpages_for):
        webpages_uri = URIRef(webpages_for + '-webpages')
        this = URIRef(webpages_uri + '-' + self.type)
        graph.add((webpages_uri, RDF.type, VCARD.Kind))
        graph.add((webpages_uri, OBO.ARG_2000029, webpages_for))
        graph.add((webpages_uri, VCARD.hasURL, this))
        graph.add((webpages_for, OBO.ARG_2000028, webpages_uri))
        graph.add((this, RDF.type, VCARD.URL))
        graph.add((this, RDFS.label, Literal(self.label)))
        graph.add((this, VCARD.url, Literal(self.url)))


class Authorship:
    def __init__(self, user=None, publication=None, rank=None):
        self.user = user
        self.publication = publication
        self.rank = rank
        self.id = id

    def add_to_graph(self, graph):
        if self.user.user_id:
            this = COLLAB_VT['authorship' +
                             self.publication.id + '-' + self.user.user_id]
            self.user.uri = COLLAB_VT[self.user.username]
        elif self.user.last_name:
            with_initial = self.user.initial is not ''
            this = COLLAB_VT['authorship' + self.publication.id +
                             self.user.name_in_uri().decode('utf-8')]
            self.user.classification = 'NonFacultyAcademic'
            self.user.uri = COLLAB_VT['person' +
                                      self.user.name_in_uri().decode('utf-8')]
            self.user.add_to_graph(graph)
        else:
            return

        self.publication.uri = COLLAB_VT['publication' + self.publication.id]
        # Person block
        graph.add((self.user.uri, VIVO.relatedBy, this))
        # Publication block
        graph.add((self.publication.uri, VIVO.relatedBy, this))
        # Relationship block
        graph.add((this, VIVO.relates, self.publication.uri))
        graph.add((this, VIVO.relates, self.user.uri))
        graph.add((this, RDF.type, VIVO['Authorship']))

        if self.rank:
            graph.add((this, VIVO.rank, Literal(self.rank, datatype=XSD.int)))


class SubjectArea:
    def __init__(self):
        self.label = None
        self.scheme = None

    def name(self):
        return 'vocab-' + self.scheme + '-' + \
            self.label.lower().replace(',', '').replace(' ', '-')

    def add_to_graph(self, graph, subject_area_of):
        this = self.uri
        graph.add((this, RDF.type, SKOS.Concept))
        graph.add((this, RDFS.label, Literal(self.label)))
        if self.scheme == 'mesh':
            graph.add(
                (this,
                 RDFS.isDefinedBy,
                 URIRef("http://www.nlm.nih.gov/mesh")))
        elif self.scheme == 'science-metrix':
            graph.add((this, RDFS.isDefinedBy, URIRef(
                "http://www.science-metrix.com/")))
        elif self.scheme == 'for':
            graph.add((this, RDFS.isDefinedBy, URIRef(
                "http://www.arc.gov.au/era/for")))
        graph.add((this, VIVO.subjectAreaOf, subject_area_of))
<file_sep>/test/test_publication.py
from unittest import TestCase

from vivotool.utils.publicationxmltordf import PublicationXml2Rdf


class TestSample(TestCase):
    def test_get_publication(self):
        pass
<file_sep>/README.md
# VIVO Harvester for Symplectic Elements

A modern, full or incremental harvester to collect data from Symplectic Elements for import into VIVO

# [VIVOHarvester](https://pypi.org/project/VIVOHarvester/)

## Installation
```
pip install VIVOHarvester
```
or
```
git clone <EMAIL>:VTUL/VIVOHarvester.git
python setup.py install
```

## Configuation
* The local.yml example file is [here](example/configfile.yml.sample)
* The logging_config.ini example file is [here](example/logging_config.ini.sample)
* The upload folder structre
```
vivouploads/
├── rdf
│   ├── photos
│   ├── publications
│   ├── relations
│   └── users
└── xml
    ├── photos
    ├── publications
    ├── relations
    ├── temp
    └── users
```
* The photo folder structure
```
harvestedImages/
├── fullImages
└── thumbnails
    └── userphoto.jpeg
```
Place the [userphoto.jpeg](harvestedImages/thumbnails/userphoto.jpeg) to harvestedImages/thumbnails/

## Usage
* Harvest data from Elements (Default)
```
vivotool -f local.yml -t harvest -d 0
```

* Harvest data from Elements a day ago (Current time -1 day)
```
vivotool -f local.yml -t harvest -d 1
```

* Import RDF data into a VIVO instance
```
vivotool -f local.yml -t ingest
```

* Fetch user_map.csv
```
vivotool -f local.yml -t getuser
```
or
```
vivotool -f local.yml -t getuser -o yourpath/yourfilename
```

## Database creation
* Create require database
```
vivotool -f local.yml -t db
```

## Upgrade to newer version
```
pip uninstall VIVOHarvester
pip install VIVOHarvester==0.1.2 (e.g.)
```

## Contributing

Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.

## Testing
```
py.test --cov=vivotool test/
coverage report -m
coverage report
```

## Versioning

We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/VTUL/VTDLP/tags).

## Authors

* Virginia Tech Libraries - Digital Libraries Development developers
	* [<NAME>](https://github.com/yinlinchen)
	* [<NAME>](https://github.com/tingtingjh)
	* [<NAME>](https://github.com/whunter)

See also the list of [contributors](https://github.com/VTUL/VTDLP/contributors) who participated in this project.

## License

This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
<file_sep>/vivotool/utils/publicationxmltordf.py
import collections
import os
import xmltodict

from rdflib import Graph, Literal, BNode, RDF, RDFS, URIRef, Namespace
from rdflib.namespace import FOAF, DC
from datetime import datetime
from vivotool.utils.models.publication_model import Publication, \
    PublicationDate, Journal, WebpagesPublisher, Authorship, SubjectArea
from vivotool.utils.models.user_model import User


class PublicationXml2Rdf(object):

    def __get_publication(self, doc):
        try:
            publication = Publication()

            # object general information
            api_obj = doc["feed"]["entry"]["api:object"]
            publication.is_public = api_obj["api:is-public"]

            if not publication.is_public:
                print("RDF graph is not generated as record is private.")
                return None

            id = api_obj["@id"]
            publication.id = id

            # Publication Types
            pub_type = doc["feed"]["entry"]["content"]["div"]["p"][0][13:][:-1]
            if pub_type == 'book chapter or section':
                pub_type = 'Chapter'
            elif pub_type == 'conference paper or presentation':
                pub_type = 'ConferencePaper'
            elif pub_type == 'internet publication':
                pub_type = 'InternetPublication'
            elif pub_type == 'journal article':
                pub_type = 'Journal'
            elif pub_type == 'numbered extension publication':
                pub_type = 'NumberedExtensionPublication'
            elif pub_type == 'poster':
                pub_type = 'ConferencePoster'
            elif pub_type == 'presentation (not at a conference)':
                pub_type = 'PresentationNotConference'
            elif pub_type == 'refereed journal article':
                pub_type = 'AcademicArticle'
            elif pub_type == 'scholarly edition':
                pub_type = 'EditorialArticle'
            elif pub_type == 'software / code':
                pub_type = 'Software'
            elif pub_type == 'thesis / dissertation':
                pub_type = 'Thesis'
            else:
                pub_type = pub_type.capitalize()
            publication.types.append(pub_type)

            # Journal
            journal = Journal()

            for obj_key in api_obj.keys():
                if obj_key == "api:repository-items":
                    # handle
                    publication.handle = api_obj[obj_key]["api:repository-item"]["api:public-url"].strip(
                    ).replace("http://hdl.handle.net/", '')
                    print('Handle: {}'.format(publication.handle))

                elif obj_key == "api:all-labels":
                    keywords = api_obj[obj_key]["api:keywords"]["api:keyword"]
                    if (isinstance(keywords, list)):
                        for keyword in keywords:
                            for key in keyword.keys():
                                if key == "@scheme":
                                    subject_area = SubjectArea()
                                    subject_area.label = keyword["#text"]
                                    subject_area.scheme = keyword[key]
                                    publication.subject_areas.append(
                                        subject_area)
                                    if keyword["@scheme"] == "mesh":
                                        publication.keywords.append(
                                            keyword["#text"])
                                    else:
                                        journal.subject_areas.append(
                                            subject_area)
                                elif key == "@source" and keyword[key] in ["manual", "pubmed"]:
                                    publication.keywords.append(
                                        keyword["#text"])
                        for keyword in publication.keywords:
                            print(
                                'Keyword: {}'.format(
                                    keyword.encode('utf-8')))

                elif obj_key == "api:records":
                    records = api_obj[obj_key]["api:record"]
                    # get the record from PubMed or first if there are many
                    # records
                    if (isinstance(records, list)):
                        record = records[0]
                        record_chosen = False
                        for r in records:
                            if r["@source-name"] == "pubmed":
                                record = r
                                record_chosen = True
                                if r["@id-at-source"]:
                                    publication.pm_id = r["@id-at-source"]
                                    print(
                                        'PubMed: {}'.format(
                                            publication.pm_id))
                            elif r["@source-name"] == "crossref":
                                if not record_chosen:
                                    record = r
                                for f in r["api:native"]["api:field"]:
                                    if f["@name"] == "doi":
                                        publication.doi = f["api:text"]
                                        print('doi: {}'.format(
                                            publication.doi))
                                    elif f["@name"] == "eissn":
                                        journal.eissn = f["api:text"]
                            elif r["@source-name"] == "dspace":
                                for f in r["api:native"]["api:field"]:
                                    if f["@name"] == "public-url":
                                        handle = f["api:text"].strip().replace(
                                            "http://hdl.handle.net/", '')
                                        if not publication.handle:
                                            publication.handle = handle
                                            print('Handle: {}'.format(
                                                publication.handle))
                                        elif int(handle.split('/')[1]) > int(publication.handle.split('/')[1]):
                                            publication.handle = handle
                                            print('Handle: {}'.format(
                                                publication.handle))
                    else:
                        record = records
                    # field info from the first record
                    publication_fields = record["api:native"]["api:field"]
                    for f in publication_fields:
                        f_name = f["@name"]
                        if f_name == "title":
                            publication.title = f["api:text"].strip('.')
                        elif f_name == "abstract":
                            publication.abstract = f["api:text"]
                        elif f_name == "pagination" and f["api:pagination"]:
                            pagination = f["api:pagination"]
                            if "api:begin-page" in pagination.keys():
                                publication.page_start = pagination["api:begin-page"]
                            if "api:end-page" in pagination.keys():
                                publication.page_end = pagination["api:end-page"]
                            else:
                                publication.page_end = publication.page_start
                        elif f_name == "issue":
                            publication.issue = f["api:text"]
                        elif f_name == "volume":
                            publication.volume = f["api:text"]
                        elif "publication-date" in f_name:
                            publication_date = PublicationDate(id)
                            date_info = f["api:date"]
                            time_precision = "Precision"
                            for key in date_info.keys():
                                if key == "api:day":
                                    time_precision = "Day" + time_precision
                                    publication_date.day = int(date_info[key])
                                elif key == "api:month":
                                    time_precision = "Month" + time_precision
                                    publication_date.month = int(
                                        date_info[key])
                                elif key == "api:year":
                                    time_precision = "year" + time_precision
                                    publication_date.year = int(date_info[key])
                            publication_date.time_precision = time_precision
                            publication_date.type = "DateTimeValue"
                            publication.publication_date = publication_date
                        elif f_name == "journal":
                            journal.label = f["api:text"]
                        elif f_name == "issn":
                            journal.issn = f["api:text"]
                        elif f_name == "publication-status":
                            publication.status = f["api:text"].lower()
                        elif f_name == "publisher-url" or f_name == "author-url":
                            if "publisher" in f_name:
                                webpages_publisher = WebpagesPublisher(
                                    'publisher')
                                webpages_publisher.label = "Publisher's Version"
                            else:
                                webpages_publisher = WebpagesPublisher(
                                    'author')
                                webpages_publisher.label = "Author's Version"
                            webpages_publisher.url = f["api:text"]
                            publication.webpages_publisher = webpages_publisher
                        elif f_name == "authors":
                            people = f["api:people"]["api:person"]
                            if (isinstance(people, list)):
                                for idx, p in enumerate(
                                        f["api:people"]["api:person"]):
                                    user = User()
                                    for key in p.keys():
                                        if key == "api:links":
                                            user.user_id = p[key]["api:link"]["@id"]
                                        elif key == "api:last-name":
                                            user.last_name = p[key].strip().replace(
                                                ' ', '-')
                                        elif key == "api:first-names":
                                            user.first_name = p[key]
                                        elif key == "api:initials":
                                            user.initial = p[key]
                                    authorship = Authorship(
                                        user, publication, idx + 1)
                                    publication.authorships.append(
                                        authorship)
                            else:
                                user = User()
                                p = people
                                for key in p.keys():
                                    if key == "api:links":
                                        user.user_id = p[key]["api:link"]["@id"]
                                    elif key == "api:last-name":
                                        user.last_name = p[key].strip().replace(
                                            ' ', '-')
                                    elif key == "api:first-names":
                                        user.first_name = p[key]
                                    elif key == "api:initials":
                                        user.initial = p[key]
                                authorship = Authorship(user, publication, 1)
                                publication.authorships.append(authorship)

            if journal.label:
                publication.journal = journal
            return publication
        except KeyError as key_error:
            print('Unable to properly parse xml file due to ' + str(key_error))
            return None

    def parse(self, fileName, target_dir=""):
        try:
            with open(fileName) as fd:
                doc = xmltodict.parse(fd.read())

            publication = self.__get_publication(doc)
            if publication:
                g = Graph()
                publication.add_to_graph(g)
                rdf_file = os.path.splitext(
                    os.path.basename(fileName))[0] + ".rdf"
                g.serialize(target_dir + rdf_file, format='nt')
                print("Resulting RDF graph in: " + rdf_file)
        except KeyError as key_error:
            print('Error parsing ' + fileName + ' due to ' + str(key_error))
<file_sep>/vivotool/harvester/elements.py
import argparse
import collections
import logging
import lxml.etree as etree
import pytz
import requests
import os
import xmltodict
import yaml

from datetime import datetime, timedelta
from vivotool.utils.file_utils import Utils
from shutil import copyfile


class Elements(object):
    """docstring for ClassName"""

    def elements_request(self, elementsurl, headers, *category):

        try:
            response = requests.get(elementsurl, headers=headers)

        except requests.exceptions.HTTPError as errh:
            print("Http Error:", errh)
        except requests.exceptions.ConnectionError as errc:
            print("Error Connecting:", errc)
        except requests.exceptions.Timeout as errt:
            print("Timeout Error:", errt)
        except requests.exceptions.RequestException as err:
            print("Something else is wrong", err)

        if category and category[0] == "photo":
            return response
        else:
            result = etree.fromstring(response.text.encode('utf-8'))
            return etree.tostring(result, pretty_print=True)

    def get_next_URL(self, content):

        if not content:
            return ""

        result_dict = xmltodict.parse(content)

        if "api:pagination" in result_dict["feed"]:
            pagination = result_dict["feed"]["api:pagination"]["api:page"]
            hasNext = False

            for p in pagination:
                if isinstance(p, collections.OrderedDict):
                    hasNext = True
                else:
                    hasNext = False

            if hasNext:
                nexturl = pagination[1]["@href"]
            else:
                nexturl = ""

        else:
            nexturl = ""

        return nexturl

    def harvest_elements_xml(
            self,
            elements_endpoint,
            headers,
            query_type,
            params,
            filename,
            *day):

        file_utils = Utils()

        if day and day[0] > 0:
            harvest_time = self.last_modified_date(day[0])
            query_url = self.createElementsQueryURL(
                elements_endpoint, query_type, params, harvest_time)
        else:
            query_url = self.createElementsQueryURL(
                elements_endpoint, query_type, params)

        if query_type == "photo":
            response = self.elements_request(query_url, headers, "photo")
            fullImages = filename + "fullImages/" + params + ".jpeg"
            thumbnails = filename + "thumbnails/" + params + ".jpeg"
            if response.status_code == 200:
                file_utils.save_photo_file(
                    response, fullImages)
                file_utils.save_photo_file(
                    response, thumbnails)
            else:
                stubphoto = filename + "thumbnails/userphoto.jpeg"
                copyfile(stubphoto, fullImages)
                copyfile(stubphoto, thumbnails)

        elif query_type == "users" or query_type == "publications" or query_type == "pubrelationships":
            response = self.elements_request(query_url, headers)
            file_utils.save_xml_file(response, filename)
            nexturl = self.get_next_URL(response)
            num = 1
            while len(nexturl) > 0:
                next_response = self.elements_request(nexturl, headers)
                next_filename = filename.replace(".xml", str(num) + ".xml")
                file_utils.save_xml_file(next_response, next_filename)
                num += 1
                nexturl = self.get_next_URL(next_response)
        else:
            response = self.elements_request(query_url, headers)
            file_utils.save_xml_file(response, filename)

    def createElementsQueryURL(
            self,
            elements_endpoint,
            query_type,
            params,
            *day):

        query_url = elements_endpoint

        if query_type == "user":
            query_url += "users/" + str(params)
        elif query_type == "users" and day:
            query_url += "users" + "?detail=full&per-page=" + \
                str(params) + "&modified-since=" + day[0]
        elif query_type == "users":
            query_url += "users" + "?detail=full&per-page=" + str(params)
        elif query_type == "publications" and day:
            query_url += "users/" + str(params) + \
                "/publications?detail=full&per-page=25&modified-since=" + day[0]
        elif query_type == "publications":
            query_url += "users/" + str(params) + \
                "/publications?detail=full&per-page=25"
        # elif query_type == "publicationbyday" and day:
        #     query_url += "publications" + "?detail=full&per-page=25" + \
        #         "&modified-since=" + day[0]
        elif query_type == "publication":
            query_url += "publications/" + str(params)
        elif query_type == "relationship":
            query_url += "relationships/" + str(params)
        elif query_type == "pubrelationships" and day:
            query_url += "publications/" + str(params) + \
                "/relationships?detail=full&per-page=25&modified-since=" + day[0]
        elif query_type == "pubrelationships":
            query_url += "publications/" + str(params) + \
                "/relationships?detail=full&per-page=25"
        elif query_type == "photo":
            query_url += "users/" + str(params) + "/photo"
        else:
            raise ValueError('Invalidated input')

        return query_url

    def last_modified_date(self, day):

        try:
            val = int(day)
        except ValueError:
            raise

        if day <= 0:
            logging.error("day should be a positive number")
            return ""

        sinceday = datetime.now() - timedelta(days=day)

        calculate_date = datetime(
            sinceday.year,
            sinceday.month,
            sinceday.day,
            sinceday.hour,
            sinceday.minute,
            sinceday.second,
            tzinfo=pytz.utc)

        return str(calculate_date.isoformat()).replace('+00:00', 'Z')
<file_sep>/vivotool/utils/__init__.py
from . import file_utils

from . import userxmltordf

from .models import user_model
<file_sep>/vivotool/utils/photo.py
import rdflib

from rdflib import Graph, Literal, BNode, RDF, RDFS, URIRef, Namespace
from rdflib.namespace import FOAF, DC


class Photo:

    def create_user_photo_graph(self, pid, eid):

        VITRO_PUBLIC = Namespace(
            "http://vitro.mannlib.cornell.edu/ns/vitro/public#")
        COLLAB_VT = Namespace("http://collab.vt.edu/vivo/individual/")

        user = COLLAB_VT[pid]
        userimage = COLLAB_VT[pid + '-image']
        imageDownload = COLLAB_VT[pid + '-imageDownload']
        imageThumbnail = COLLAB_VT[pid + '-imageThumbnail']
        imageThumbnailDownload = COLLAB_VT[pid + '-imageThumbnailDownload']

        thingURL = URIRef("http://www.w3.org/2002/07/owl#Thing")
        fileURL = URIRef(
            "http://vitro.mannlib.cornell.edu/ns/vitro/public#File")
        fileByteStreamURL = URIRef(
            "http://vitro.mannlib.cornell.edu/ns/vitro/public#FileByteStream")

        jpegmimeType = Literal("image/jpeg")
        filename = Literal(eid + ".jpeg")
        directDownloadUrl = Literal(
            "/harvestedImages/fullImages/" + eid + ".jpeg")
        thumbnailDownloadUrl = Literal(
            "/harvestedImages/thumbnails/" + eid + ".jpeg")

        g = Graph()

        g.bind('vitro-public', VITRO_PUBLIC)

        g.add((user, VITRO_PUBLIC.mainImage, userimage))

        g.add((userimage, RDF.type, thingURL))
        g.add((userimage, RDF.type, fileURL))
        g.add((userimage, VITRO_PUBLIC.downloadLocation, imageDownload))
        g.add((userimage, VITRO_PUBLIC.thumbnailImage, imageThumbnail))
        g.add((userimage, VITRO_PUBLIC.filename, filename))
        g.add((userimage, VITRO_PUBLIC.mimeType, jpegmimeType))

        g.add((imageDownload, RDF.type, fileByteStreamURL))
        g.add(
            (imageDownload,
             VITRO_PUBLIC.directDownloadUrl,
             directDownloadUrl))

        g.add((imageThumbnail, RDF.type, thingURL))
        g.add((imageThumbnail, RDF.type, fileURL))
        g.add(
            (imageThumbnail,
             VITRO_PUBLIC.downloadLocation,
             imageThumbnailDownload))
        g.add((imageThumbnail, VITRO_PUBLIC.filename, filename))
        g.add((imageThumbnail, VITRO_PUBLIC.mimeType, jpegmimeType))

        g.add((imageThumbnailDownload, RDF.type, fileByteStreamURL))
        g.add(
            (imageThumbnailDownload,
             VITRO_PUBLIC.directDownloadUrl,
             thumbnailDownloadUrl))

        return g.serialize(format='nt')
<file_sep>/test/test_harvester.py
import requests
import requests_mock
import tempfile
import os

from os import path
from unittest import TestCase
from vivotool.harvester.elements import Elements
from vivotool.utils.file_utils import Utils
from datetime import datetime, timedelta
from unittest import mock


class TestElements(TestCase):

    def setUp(self):
        self.elements = Elements()
        self.utils = Utils()
        self.test_dir = tempfile.mkdtemp()
        os.makedirs(self.test_dir + "/fullImages/")
        os.makedirs(self.test_dir + "/thumbnails/")

    def tearDown(self):
        self.elements = None

    @requests_mock.mock()
    def test_request_elements(self, m):
        m.get('http://foo.com/photo', text='photo')
        m.get('http://foo.com/user/1003', text='<feed>test</feed>')
        output = self.elements.elements_request(
            'http://foo.com/photo', "", "photo")
        self.assertEqual(output.text, "photo")

        output = self.elements.elements_request("http://foo.com/user/1003", "")
        self.assertEqual(output.decode("utf-8"), "<feed>test</feed>\n")

    def test_get_next_URL(self):

        output = self.elements.get_next_URL("")

        # check empty string
        self.assertEqual(output, "")

        test_xml_content = """<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="testapi">
                              <api:pagination results-count="9429" items-per-page="25">
                                <api:page position="this" href="https://test.com"/>
                                <api:page position="next" href="https://test.com?after-id=2784"/>
                              </api:pagination>
                            </feed>
                            """

        output = self.elements.get_next_URL(test_xml_content)
        expected = "https://test.com?after-id=2784"
        self.assertEqual(output, expected)

        # test if there is no next URL
        test_xml_content = """<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="testapi">
                              <api:pagination results-count="9429" items-per-page="25">
                                <api:page position="this" href="https://test.com"/>
                              </api:pagination>
                            </feed>
                            """

        output = self.elements.get_next_URL(test_xml_content)
        expected = ""
        self.assertEqual(output, expected)

        test_xml_content = """<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="testapi">
                            </feed>
                            """
        output = self.elements.get_next_URL(test_xml_content)
        expected = ""
        self.assertEqual(output, expected)

    def mock_queryurl(elements_endpoint, query_type, params, *day):
        return "http://foo.com/" + query_type + "/" + params

    def mock_elements_request(elementsurl, headers, *category):
        class MockResponse:
            def __init__(self, data, status_code):
                self.data = data
                self.status_code = status_code

        if category and category[0] == "photo":
            return MockResponse("<feed>test</feed>\n", 200)
        else:
            return "<feed>test</feed>\n"

    def mock_save_photo_file(content, filename):
        content = "<feed>test</feed>\n"
        with open(filename, "w") as file:
            file.write(content)

    @mock.patch(
        'vivotool.harvester.elements.Elements.createElementsQueryURL',
        side_effect=mock_queryurl)
    @mock.patch(
        'vivotool.harvester.elements.Elements.elements_request',
        side_effect=mock_elements_request)
    @mock.patch(
        'vivotool.utils.file_utils.Utils.save_photo_file',
        side_effect=mock_save_photo_file)
    def test_harvest_elements_xml(self, m1, m2, m3):

        # test query_type == "photo"
        self.elements.harvest_elements_xml(
            'http://foo.com/', "", "photo", "1003", self.test_dir + "/")

        imagefile1 = path.join(self.test_dir, 'fullImages/1003.jpeg')
        imagefile2 = path.join(self.test_dir, 'thumbnails/1003.jpeg')
        output = self.utils.read_file(imagefile1)
        self.assertEqual(output, "<feed>test</feed>\n")
        output = self.utils.read_file(imagefile2)
        self.assertEqual(output, "<feed>test</feed>\n")

        # test query_type == "users"
        testfile = path.join(self.test_dir, 'testuser.xml')
        self.elements.harvest_elements_xml(
            'http://foo.com/', "", "users", "1003", testfile)
        output = self.utils.read_file(testfile)
        self.assertEqual(output, "<feed>test</feed>\n")

        # test query_type == "publications" with day
        testfile = path.join(self.test_dir, 'testpublications.xml')
        self.elements.harvest_elements_xml(
            'http://foo.com/', "", "publications", "9003", testfile, 10)
        output = self.utils.read_file(testfile)
        self.assertEqual(output, "<feed>test</feed>\n")

        # test query_type = "user"
        testfile = path.join(self.test_dir, 'testsingleuser.xml')
        self.elements.harvest_elements_xml(
            'http://foo.com/', "", "user", "1003", testfile)
        output = self.utils.read_file(testfile)
        self.assertEqual(output, "<feed>test</feed>\n")

    def test_createElementsQueryURL(self):

        elements_endpoint = "http://foo.com"

        # check user queryurl
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "user", 1033)
        self.assertEqual(output, elements_endpoint + "users/1033")

        # check users and day
        harvest_time = self.elements.last_modified_date(5)[:10]
        expected = str(datetime.now() - timedelta(days=5))[:10]
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "users", 25, harvest_time)
        self.assertEqual(
            output,
            elements_endpoint +
            "users?detail=full&per-page=25&modified-since=" +
            expected)

        # check users queryurl
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "users", 25)
        self.assertEqual(
            output,
            elements_endpoint +
            "users?detail=full&per-page=25")

        # check publications and day
        harvest_time = self.elements.last_modified_date(5)[:10]
        expected = str(datetime.now() - timedelta(days=5))[:10]
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "publications", 1033, harvest_time)
        self.assertEqual(
            output,
            elements_endpoint +
            "users/1033/publications?detail=full&per-page=25&modified-since=" +
            expected)

        # check publications
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "publications", 1033)
        self.assertEqual(output, elements_endpoint +
                         "users/1033/publications?detail=full&per-page=25")

        # check publication
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "publication", 1033)
        self.assertEqual(output, elements_endpoint + "publications/1033")

        # check relationship
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "relationship", 1033)
        self.assertEqual(output, elements_endpoint + "relationships/1033")

        # check pubrelationships and day
        harvest_time = self.elements.last_modified_date(5)[:10]
        expected = str(datetime.now() - timedelta(days=5))[:10]
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "pubrelationships", 1033, harvest_time)
        self.assertEqual(
            output,
            elements_endpoint +
            "publications/1033/relationships?detail=full&per-page=25&modified-since=" +
            expected)

        # check pubrelationships
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "pubrelationships", 1033)
        self.assertEqual(
            output,
            elements_endpoint +
            "publications/1033/relationships?detail=full&per-page=25")

        # check photo
        output = self.elements.createElementsQueryURL(
            elements_endpoint, "photo", 1033)
        self.assertEqual(output, elements_endpoint + "users/1033/photo")

        # check invalidated input
        self.assertRaises(
            ValueError,
            lambda: self.elements.createElementsQueryURL(
                elements_endpoint, "xyz", 1033))

    def test_last_modified_date(self):

        day = 1
        output = self.elements.last_modified_date(day)[:10]
        expected = str(datetime.now() - timedelta(days=day))[:10]

        self.assertEqual(output, expected)

        day = "string"
        self.assertRaises(
            ValueError,
            lambda: self.elements.last_modified_date(day)[
                :1])

        day = "1"
        self.assertRaises(
            TypeError,
            lambda: self.elements.last_modified_date(day)[
                :1])

        day = -1
        output = self.elements.last_modified_date(day)[:10]
        expected = ""

        self.assertEqual(output, expected)
<file_sep>/vivotool/utils/models/user_model.py
#!/usr/bin/env python

"""
User Model

Modified + Inspired by:
  https://github.com/andreasf/pentabarf-rdf/
"""
import re
from rdflib import Graph, BNode, Namespace, Literal, URIRef, RDF, RDFS, URIRef, XSD
from rdflib.namespace import FOAF, DC, RDFS, RDF

COLLAB_VT = Namespace("http://collab.vt.edu/vivo/individual/")
VCARD = Namespace('http://www.w3.org/2006/vcard/ns#')
VIVO = Namespace('http://vivoweb.org/ontology/core#')
OBO = Namespace('http://purl.obolibrary.org/obo/')


class User:
    def __init__(
            self,
            title=None,
            first_name=None,
            last_name=None,
            username=None):
        self.title = title
        self.username = username
        self.user_id = None
        self.first_name = first_name
        self.last_name = last_name
        self.initial = None
        self.classification = 'FacultyMember'
        self.suffix = ''
        self.overview = ''
        self.email = ''
        self.email_is_public = False
        self.degrees = []
        self.institutions = []
        self.appointments = []
        self.vcard = VCard(self)
        self.is_academic = False
        self.is_current_staff = False
        self.is_public = False

    def id(self):
        return self.username

    def display_name(self):
        # TODO: need further check
        # if not self.first_name:
        #     return

        if self.last_name and self.first_name:
            return '{}, {}'.format(
                self.last_name.encode('utf-8'),
                self.first_name.encode('utf-8'))
        elif self.last_name and self.initial:
            return '{}, {}'.format(
                self.last_name.encode('utf-8'),
                self.initial.encode('utf-8'))
        elif self.last_name:
            return '{}'.format(self.last_name.encode('utf-8'))

    def name_in_uri(self):
        if self.last_name and self.initial:
            name = '-{}-{}'.format(self.last_name.encode('utf-8'),
                                   self.initial.encode('utf-8'))
        elif self.last_name:
            return '-{}'.format(self.last_name.encode('utf-8'))
        name = re.sub(r'["<>#%\{\}\|\\\^~\[\]`]', '', name)
        return name.lower().replace(' ', '-')

    def add_to_graph(self, graph):

        if self.classification == 'NonFacultyAcademic':
            user = self.uri
        else:
            if self.display_name() is None:
                return

            user = COLLAB_VT[self.id()]

            self.__add_bindings(graph)
            graph.add((user, VIVO.overview, Literal(self.overview)))

            for appointments in self.appointments:
                appointments.add_to_graph(graph, self)

            for degree in self.degrees:
                degree.add_to_graph(graph, self)

        graph.add((user, RDF.type, VIVO[self.classification]))
        graph.add(
            (user, RDFS.label, Literal(
                self.display_name().decode('utf-8'))))
        self.vcard.add_to_graph(graph)

    def __add_bindings(self, graph):
        graph.bind('vivo', VIVO)
        graph.bind('vcard', VCARD)
        graph.bind('obo', OBO)


class Institution:
    def __init__(self):
        self.label = None
        self.city = None
        self.country = None
        self.dept = None

    def add_to_graph(self, graph):
        institution = COLLAB_VT[self.id()]

        graph.add((institution, RDF.type, VIVO['University']))
        graph.add((institution, RDFS.label, Literal(self.label)))
        if self.dept:
            dept = COLLAB_VT[self.dept_id()]
            graph.add((institution, OBO.BFO_0000051, dept))
            graph.add((dept, OBO.BFO_0000050, institution))
            graph.add((dept, RDF.type, VIVO['AcademicDepartment']))
            graph.add((dept, RDFS.label, Literal(self.dept)))

    def id(self):
        return 'institution-{}'.format(self.label.lower().replace(' ', '-'))

    def dept_id(self):
        return 'dept-{}-{}'.format(self.dept.lower().replace(' ', '-'),
                                   self.label.lower().replace(' ', '-'))


class Degree:
    def __init__(self, username=None, type=None, label=None, field=''):
        self.username = username
        self.type = type
        self.label = label
        self.field = field
        self.institution = Institution()
        self.start_date = None
        self.end_date = None
        self.date_interval = None
        self.is_public = False

    def add_to_graph(self, graph, user_obj):
        degree = COLLAB_VT[self.degree_id()]
        degree_institution = COLLAB_VT[self.institution.id()]
        awarded_degree = COLLAB_VT[self.individual_degree_id()]
        user = COLLAB_VT[user_obj.id()]

        graph.add((user, VIVO.relatedBy, degree))
        graph.add((degree, RDF.type, VIVO['AcademicDegree']))
        graph.add((degree, RDFS.label, Literal(self.label)))
        graph.add((awarded_degree, RDF.type, VIVO['AwardedDegree']))
        graph.add((awarded_degree, VIVO.relates, COLLAB_VT[self.degree_id()]))
        graph.add((awarded_degree, VIVO.relates, COLLAB_VT[self.username]))
        graph.add((awarded_degree, OBO.RO_0002353,
                   COLLAB_VT[self.eduprocess_id()]))
        graph.add(
            (awarded_degree,
             RDFS.label,
             Literal(
                 '{}: {}'.format(
                     user_obj.display_name().decode('utf-8'),
                     self.label))))
        graph.add((awarded_degree, VIVO.assignedBy, degree_institution))
        graph.add((degree_institution, VIVO.relatedBy, awarded_degree))

        self.institution.add_to_graph(graph)
        self.__add_dates_to_graph(graph)
        self.__add_eduprocess_to_graph(graph, user_obj)

    def __add_eduprocess_to_graph(self, graph, user):
        edu_process = COLLAB_VT[self.eduprocess_id()]
        degree_institution = COLLAB_VT[self.institution.id()]
        awarded_degree = COLLAB_VT[self.individual_degree_id()]

        graph.add((edu_process, RDF.type, VIVO['EducationalProcess']))
        graph.add((edu_process, VIVO.dateTimeInterval,
                   COLLAB_VT[self.date_time_interval_id()]))
        graph.add((edu_process, OBO.RO_0000057, degree_institution))
        graph.add((edu_process, OBO.RO_0002234, awarded_degree))
        graph.add((edu_process, OBO.RO_0000057, COLLAB_VT[user.id()]))

    def __add_dates_to_graph(self, graph):
        if self.start_date or self.end_date:
            degree_start_date = COLLAB_VT[self.individual_degree_id(
            ) + '-startDate']
            degree_end_date = COLLAB_VT[self.individual_degree_id(
            ) + '-endDate']
            degree_interval = COLLAB_VT[self.date_time_interval_id()]

            graph.add((degree_interval, RDF.type, VIVO['DateTimeInterval']))
            if self.start_date:
                self.__add_degree_date_to_graph(
                    graph, degree_start_date, self.start_date)
                graph.add((degree_interval, VIVO.start, degree_start_date))
            if self.end_date:
                self.__add_degree_date_to_graph(
                    graph, degree_end_date, self.end_date)
                graph.add((degree_interval, VIVO.end, degree_end_date))

    def __add_degree_date_to_graph(self, graph, subject, date):
        graph.add((subject, RDF.type, VIVO['DateTimeValue']))
        graph.add(
            (subject,
             VIVO.dateTime,
             Literal(
                 date,
                 datatype=XSD.dateTime)))
        graph.add((subject, VIVO.dateTimePrecision, VIVO['yearPrecision']))

    def degree_id(self):
        # example: 'degree-ma-geography'
        return 'degree-' + self.type.lower().replace(' ', '-') + \
            '-' + self.field.lower().replace(' ', '-')

    def individual_degree_id(self):
        # example: 'degree-username-ma-geography'
        return 'degree-{}-{}-{}'.format(self.username.lower(),
                                        self.type.lower().replace(' ', '-'),
                                        self.field.lower().replace(' ', '-'))

    def date_time_interval_id(self):
        # example: 'degree-username-ma-geography-dateInterval-2000-2001'
        # TODO: find a better way to store, parse out years for the datetime interval
        # Assumption: Will return different format if both start and end dates
        # aren't defined
        from datetime import datetime
        if self.start_date and self.end_date:
            start_year = datetime.strptime(
                self.start_date, '%Y-%m-%dT%H:%M:%S').year
            end_year = datetime.strptime(
                self.end_date, '%Y-%m-%dT%H:%M:%S').year
            return '{}-dateInterval-{}-{}'.format(
                self.individual_degree_id(), start_year, end_year)
        else:
            return self.individual_degree_id() + '-dateInterval'

    def eduprocess_id(self):
        # example: 'eduprocess-username-ma-geography'
        return 'eduprocess-{}-{}-{}'.format(
            self.username.lower(), self.type.lower().replace(
                ' ', '-'), self.field.lower().replace(' ', '-'))


class Appointment:
    def __init__(self, is_public=False, id_suffix=''):
        self.is_public = is_public
        self.id_suffix = id_suffix
        self.label = None
        self.institution = Institution()
        self.start_date = None
        self.end_date = None

    def add_to_graph(self, graph, user_obj):
        if (self.is_public is False):
            return

        appointment = COLLAB_VT[self.id()]
        user = COLLAB_VT[user_obj.id()]

        graph.add((appointment, RDF.type, VIVO['Position']))
        graph.add((appointment, RDFS.label, Literal(self.label)))
        self.__add_interval_to_graph(graph, appointment)
        self.institution.add_to_graph(graph)
        if self.institution.dept:
            graph.add((appointment, VIVO.relates,
                       COLLAB_VT[self.institution.dept_id()]))
        graph.add((user, VIVO.relatedBy, appointment))
        graph.add((appointment, VIVO.relates, user))

    def id(self):
        return 'appointment-{}'.format(self.id_suffix)

    def date_interval_id(self):
        return '{}-dateInterval'.format(self.id())

    def __add_interval_to_graph(self, graph, appointment):
        if (self.start_date or self.end_date):
            interval = COLLAB_VT[self.date_interval_id()]
        graph.add((appointment, VIVO.dateTimeInterval, interval))
        graph.add((interval, RDF.type, VIVO['DateTimeInterval']))
        if self.start_date:
            start_date = COLLAB_VT[self.id() + '-startDate']
            graph.add((interval, VIVO.start, start_date))
            self.__add_date_to_graph(graph, start_date, self.start_date)
        if self.end_date:
            end_date = COLLAB_VT[self.id() + '-endDate']
            graph.add((interval, VIVO.end, end_date))
            self.__add_date_to_graph(graph, end_date, self.end_date)

    def __add_date_to_graph(self, graph, subject, date):
        graph.add((subject, RDF.type, VIVO['DateTimeValue']))
        graph.add(
            (subject,
             VIVO.dateTime,
             Literal(
                 date,
                 datatype=XSD.dateTime)))
        graph.add((subject, VIVO.dateTimePrecision, VIVO['yearPrecision']))


class VCard:
    def __init__(self, user=None):
        self.user = user
        if user is None:
            user = User()

    def add_to_graph(self, graph):
        self.__add_vcard(graph)

    def __add_vcard(self, graph):
        if self.user.classification == 'NonFacultyAcademic':
            user = self.user.uri
            # person vcard
            vt_vcard = COLLAB_VT['personvcard' +
                                 self.user.name_in_uri().decode('utf-8')]
            vt_vcardname = COLLAB_VT['personvcardname' +
                                     self.user.name_in_uri().decode('utf-8')]
        else:
            user = COLLAB_VT[self.user.username]

            # vcard
            vt_vcard = COLLAB_VT['vcard-' + self.user.username]

            # vcardemail
            vt_vcardemail = COLLAB_VT['vcardemail-' + self.user.username]
            if self.user.email_is_public:
                graph.add((vt_vcard, VCARD.hasEmail, vt_vcardemail))
                graph.add((vt_vcardemail, RDF.type, VCARD['Email']))
                graph.add((vt_vcardemail, RDF.type, VCARD['Work']))
                graph.add(
                    (vt_vcardemail,
                     VCARD.email,
                     Literal(
                         self.user.email)))

            # vcardname
            # TODO: Which fields are mandatory? Which fields are optional?
            vt_vcardname = COLLAB_VT['vcardname-' + self.user.username]
            if self.user.suffix:
                graph.add(
                    (vt_vcardname,
                     VCARD.suffix,
                     Literal(
                         self.user.suffix)))

            # vcardtitle
            vt_vcardtitle = COLLAB_VT['vcardtitle-' + self.user.username]
            graph.add((vt_vcard, VCARD.hasTitle, vt_vcardtitle))
            graph.add((vt_vcardtitle, RDF.type, VCARD['Title']))
            graph.add((vt_vcardtitle, VCARD.title, Literal(self.user.title)))
        graph.add((user, OBO.ARG_2000028, vt_vcard))
        graph.add((vt_vcard, RDF.type, VCARD.Individual))
        graph.add((vt_vcard, OBO.ARG_2000029, user))
        graph.add((vt_vcard, VCARD.hasName, vt_vcardname))
        graph.add((vt_vcardname, RDF.type, VCARD.Name))
        graph.add(
            (vt_vcardname,
             VCARD.givenName,
             Literal(
                 self.user.first_name)))
        graph.add(
            (vt_vcardname,
             VCARD.familyName,
             Literal(
                 self.user.last_name)))
