Coverage for teiphy/witness.py: 100.00%

30 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2025-01-15 16:06 +0000

1#!/usr/bin/env python3 

2 

3from datetime import datetime # for calculating the current year (for dating purposes) 

4from lxml import etree as et 

5 

6from .common import xml_ns, tei_ns 

7 

8 

9class Witness: 

10 """Base class for storing TEI XML witness data internally. 

11 

12 This corresponds to a witness element in the collation. 

13 

14 Attributes: 

15 id: The ID string of this Witness. It should be unique. 

16 type: A string representing the type of witness. Examples include "corrector", "version", and "father". 

17 date_range: A list containing a low and high date for this Witness. 

18 """ 

19 

20 def __init__(self, xml: et.Element, verbose: bool = False): 

21 """Constructs a new Witness instance from the TEI XML input. 

22 

23 Args: 

24 xml: A witness element. 

25 verbose: An optional flag indicating whether or not to print status updates. 

26 """ 

27 # Use its xml:id if it has one; otherwise, use its n attribute if it has one; otherwise, use its text: 

28 self.id = "" 

29 if xml.get("{%s}id" % xml_ns) is not None: 

30 self.id = xml.get("{%s}id" % xml_ns) 

31 elif xml.get("n") is not None: 

32 self.id = xml.get("n") 

33 else: 

34 self.id = xml.text 

35 # If it has a type, then save that; otherwise, default to "manuscript": 

36 self.type = xml.get("type") if xml.get("type") is not None else "manuscript" 

37 # If it has an origDate descendant, then use the dates in its attributes: 

38 self.date_range = [None, datetime.now().year] 

39 for orig_date in xml.xpath(".//tei:origDate", namespaces={"tei": tei_ns}): 

40 date_range = [None, datetime.now().year] 

41 # Try the @when attribute first; if it is set, then it accounts for both ends of the date range: 

42 if orig_date.get("when") is not None: 

43 date_range[0] = int(orig_date.get("when").split("-")[0]) 

44 date_range[1] = date_range[0] 

45 # Failing that, if it has @from and @to attributes (indicating the period over which the manuscript was completed), 

46 # then the completion date of the work accounts for both ends of the date range: 

47 elif orig_date.get("to") is not None: 

48 date_range[0] = int(orig_date.get("to").split("-")[0]) 

49 date_range[1] = date_range[0] 

50 # Failing that, set lower and upper bounds on the witness's date using the the @notBefore and @notAfter attributes: 

51 elif orig_date.get("notBefore") is not None or orig_date.get("notAfter") is not None: 

52 if orig_date.get("notBefore") is not None: 

53 date_range[0] = int(orig_date.get("notBefore").split("-")[0]) 

54 if orig_date.get("notAfter") is not None: 

55 date_range[1] = int(orig_date.get("notAfter").split("-")[0]) 

56 self.date_range = date_range 

57 break 

58 

59 if verbose: 

60 print("New Witness (id: %s, type: %s, date_range: %s)" % (self.id, self.type, str(self.date_range)))