Repository: dag-hammarskjold-library/dlx

Issue:
Enable Marc.get_values to return all subfield values if no subfield is specified

Discussion:

Solution patch:
diff --git a/dlx/marc/__init__.py b/dlx/marc/__init__.py
index ace6197c..e6aaf898 100644
--- a/dlx/marc/__init__.py
+++ b/dlx/marc/__init__.py
@@ -594,7 +594,7 @@ def get_field(self, tag, place=0):
     def get_values(self, tag, *codes, **kwargs):
         if tag[:2] == '00':
             return [field.value for field in self.get_fields(tag)]
-            
+           
         values = [sub.value for sub in self.get_subfields(tag, *codes, place=kwargs.get('place'))]
 
         return list(filter(None, values))
@@ -661,6 +661,9 @@ def get_subfields(self, tag, *codes, **kwargs):
         for field in fields:
             if isinstance(field, Controlfield):
                 return
+            
+            if not codes:
+                codes = list(set([x.code for x in field.subfields]))
 
             subs += list(filter(lambda x: x.code in codes, field.subfields))
 
@@ -2186,6 +2189,9 @@ def get_value(self, code, *, place=0):
         return sub.value if sub else ''
 
     def get_values(self, *codes):
+        if not codes:
+            codes = list(set([x.code for x in self.subfields]))
+
         values = []
 
         for code in codes:
@@ -2214,6 +2220,11 @@ def get_subfield(self, code, place=None):
             if i == place:
                 return sub
 
+    def delete_subfield(self, code: str):
+        self.subfields = [x for x in self.subfields if x != code]
+
+        return self
+
     def set(self, code, new_val, *, ind1=None, ind2=None, place=0, auth_control=True):
         if not new_val and not ind1 and not ind2:
             return self
diff --git a/dlx/marc/query.py b/dlx/marc/query.py
index fc0ab175..47abc1b2 100644
--- a/dlx/marc/query.py
+++ b/dlx/marc/query.py
@@ -181,6 +181,8 @@ def parse(token, modifier=None):
                     for m in matches:
                         matched_subfield_values += [x['value'] for x in filter(lambda z: z['code'] == code, m['subfields'])]
 
+                    matched_subfield_values = list(filter(None, matched_subfield_values))
+
                     stemmed_terms, filtered = Tokenizer.tokenize(value), []
 
                     for val in matched_subfield_values:
@@ -302,7 +304,7 @@ def parse(token, modifier=None):
                     for m in matches:
                         matched_subfield_values += [x['value'] for x in m['subfields']]
 
-                    matched_subfield_values = list(set(matched_subfield_values))
+                    matched_subfield_values = list(set(filter(None, matched_subfield_values)))
                     stemmed_terms, filtered = Tokenizer.tokenize(value), []
 
                     for val in matched_subfield_values:
diff --git a/requirements.txt b/requirements.txt
index 7a39e773..f893433e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,8 @@
-boto3==1.35.77
-certifi==2024.8.30
+boto3==1.35.83
+certifi==2024.12.14
 jsonschema==4.23.0
 mongomock==4.3.0
-moto==5.0.22
+moto==5.0.23
 nltk==3.9.1
 pymongo==4.10.1
 pytest==8.3.4


Test patch:
diff --git a/tests/test_marc.py b/tests/test_marc.py
index ef93d5bd..4160c706 100644
--- a/tests/test_marc.py
+++ b/tests/test_marc.py
@@ -502,7 +502,16 @@ def test_get_value(db, bibs):
     assert bib.get_values('520', 'a', place=1) == ['Another description', 'Repeated subfield']
     assert bib.get_value('999', 'a') == ''
     assert bib.get_values('999', 'a') == []
-    
+
+def test_get_values(db, bibs):
+    from dlx.marc import Bib
+
+    bib = Bib(bibs[0])
+    assert bib.get_values('520') == ['Description', 'Another description', 'Repeated subfield']
+
+    # field level
+    assert bib.get_field('520', place=1).get_values() == ['Another description', 'Repeated subfield']
+
 def test_get_xref(db, bibs):
     from dlx.marc import Bib
     
@@ -985,4 +994,17 @@ def test_history(db):
             datetime.fromisoformat('2999-12-31') # set in the future because the delete is too fast here
         ), 
         None
-    ) == bib.id
\ No newline at end of file
+    ) == bib.id
+
+def test_auth_deleted_subfield(db):
+    from dlx.marc import Bib, Auth, Query
+
+    # sequence of events formerly causing an error
+    auth = Auth().set('110', 'a', 'will NOT be deleted').set('110', 'z', 'will be deleted').commit()
+    bib = Bib().set('710', 'a', auth.id).set('710', 'z', auth.id).commit()
+    auth.heading_field.delete_subfield('z')
+    auth.commit()
+    bib.set('999', 'a', 'updated').commit()
+
+    q = Query.from_string('710:"will not be deleted"')
+    assert q.to_json()
\ No newline at end of file