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
« prev ^ index » next coverage.py v6.5.0, created at 2025-01-15 16:06 +0000
1#!/usr/bin/env python3
3from datetime import datetime # for calculating the current year (for dating purposes)
4from lxml import etree as et
6from .common import xml_ns, tei_ns
9class Witness:
10 """Base class for storing TEI XML witness data internally.
12 This corresponds to a witness element in the collation.
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 """
20 def __init__(self, xml: et.Element, verbose: bool = False):
21 """Constructs a new Witness instance from the TEI XML input.
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
59 if verbose:
60 print("New Witness (id: %s, type: %s, date_range: %s)" % (self.id, self.type, str(self.date_range)))