Skip to content

Commit 34b1b3b

Browse files
committed
Add shared module for handling obsolescence chains / revisions
1 parent ca3f00f commit 34b1b3b

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# This work was created by participants in the DataONE project, and is
4+
# jointly copyrighted by participating institutions in DataONE. For
5+
# more information on DataONE, see our web site at http://dataone.org.
6+
#
7+
# Copyright 2009-2016 DataONE
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License");
10+
# you may not use this file except in compliance with the License.
11+
# You may obtain a copy of the License at
12+
#
13+
# http://www.apache.org/licenses/LICENSE-2.0
14+
#
15+
# Unless required by applicable law or agreed to in writing, software
16+
# distributed under the License is distributed on an "AS IS" BASIS,
17+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
# See the License for the specific language governing permissions and
19+
# limitations under the License.
20+
"""Utilities for working with revision / obsolescence chains
21+
"""
22+
from __future__ import absolute_import
23+
24+
import d1_common.util
25+
import d1_common.xml
26+
27+
28+
def get_identifiers(sysmeta_pyxb):
29+
"""Return: (pid, sid, obsoletes_pid, obsoleted_by_pid)
30+
"""
31+
pid = d1_common.xml.get_opt_val(sysmeta_pyxb, 'identifier')
32+
sid = d1_common.xml.get_opt_val(sysmeta_pyxb, 'seriesId')
33+
obsoletes_pid = d1_common.xml.get_opt_val(sysmeta_pyxb, 'obsoletes')
34+
obsoleted_by_pid = d1_common.xml.get_opt_val(sysmeta_pyxb, 'obsoletedBy')
35+
return pid, sid, obsoletes_pid, obsoleted_by_pid
36+
37+
38+
def topological_sort(unsorted_dict):
39+
"""Sort objects by dependency
40+
41+
{unconnected_dict} is a dict of PID to obsoleted PID.
42+
43+
Return:
44+
sorted_list: A list of PIDs ordered so that all PIDs that obsolete an object
45+
are listed after the object they obsolete.
46+
unconnected_dict: A dict of PID to obsoleted PID of any objects that could not
47+
be added to a revision chain.
48+
49+
{obsoletes_dict} is modified by the sort and on return holds any items that
50+
could not be sorted. These items will have obsoletes PIDs that directly or
51+
indirectly reference a PID that could not be sorted.
52+
53+
The sort works by repeatedly iterating over an unsorted list of PIDs and
54+
moving PIDs to the sorted list as they become available. A PID is available to
55+
be moved to the sorted list if it does not obsolete a PID or if the PID it
56+
obsoletes is already in the sorted list.
57+
"""
58+
sorted_list = []
59+
sorted_set = set()
60+
found = True
61+
unconnected_dict = unsorted_dict.copy()
62+
while found:
63+
found = False
64+
for pid, obsoletes_pid in unconnected_dict.items():
65+
if obsoletes_pid is None or obsoletes_pid in sorted_set:
66+
found = True
67+
sorted_list.append(pid)
68+
sorted_set.add(pid)
69+
del unconnected_dict[pid]
70+
return sorted_list, unconnected_dict
71+
72+
73+
def get_pids_in_revision_chain(client, did):
74+
"""Given a SID or a PID of any object in a chain, return a list of all PIDs in
75+
the chain. The returned list is in the same order as the chain. The initial
76+
PID is typically obtained by resolving a SID. If the given PID is not in a
77+
chain, a list containing the single object is returned.
78+
"""
79+
80+
def req(p):
81+
return d1_common.xml.get_req_val(p)
82+
83+
def opt(p, a):
84+
return d1_common.xml.get_opt_val(p, a)
85+
86+
sysmeta_pyxb = client.getSystemMetadata(did)
87+
# Walk to tail
88+
while opt(sysmeta_pyxb, 'obsoletes'):
89+
sysmeta_pyxb = client.getSystemMetadata(opt(sysmeta_pyxb, 'obsoletes'))
90+
chain_pid_list = [req(sysmeta_pyxb.identifier)]
91+
# Walk from tail to head, recording traversed PIDs
92+
while opt(sysmeta_pyxb, 'obsoletedBy'):
93+
sysmeta_pyxb = client.getSystemMetadata(opt(sysmeta_pyxb, 'obsoletedBy'))
94+
chain_pid_list.append(req(sysmeta_pyxb.identifier))
95+
return chain_pid_list

lib_common/src/d1_common/util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,11 @@ def count(self, event_str, inc_int=1):
143143
self._event_dict.setdefault(event_str, 0)
144144
self._event_dict[event_str] += inc_int
145145

146-
def log_and_count(self, event_str, log_str=None, inc_int=1):
146+
def log_and_count(self, event_str, msg_str=None, inc_int=1):
147147
"""{event_str} is both a key for the count and part of the log message.
148148
{log_str} is a message with details that may change for each call
149149
"""
150-
logging.info(event_str + ('. ' + log_str if log_str else ''))
150+
logging.info(u'{} - {}: {}'.format(event_str, msg_str, inc_int))
151151
self.count(event_str, inc_int)
152152

153153
def dump_to_log(self):

0 commit comments

Comments
 (0)