ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/ns_dev/Python/NinoCode/Active_prgs/Redgrave/RelativityAuditFlattenerXml.py
(Generate patch)

Comparing Python/NinoCode/Active_prgs/Redgrave/RelativityAuditFlattenerXml.py (file contents):
Revision 961 by nino.borges, Mon Dec 8 22:26:25 2025 UTC vs.
Revision 962 by nino.borges, Mon Dec 8 23:06:33 2025 UTC

# Line 10 | Line 10 | Using my XML inspector, this program wil
10  
11   """
12  
13
13   import csv
14   import xml.etree.ElementTree as ET
15 < from typing import Any, Dict, List
15 > from typing import Any, Dict, List, Optional
16 >
17 >
18 > # ------------------------------------------------------------
19 > # Helper: load choice map (artifactID -> choice name)
20 > # ------------------------------------------------------------
21 >
22 > def load_choice_map(
23 >    choice_csv_path: str,
24 >    artifact_id_col: str = "ArtifactID",
25 >    name_col: str = "Name",
26 > ) -> Dict[str, str]:
27 >    """
28 >    Load a CSV containing workspace choices into a dict:
29 >        { artifact_id (str) -> choice_name (str) }
30 >
31 >    By default expects columns named:
32 >        ArtifactID, Name
33 >
34 >    If your file uses different headers, adjust artifact_id_col / name_col.
35 >    """
36 >    choice_map: Dict[str, str] = {}
37 >
38 >    with open(choice_csv_path, newline="", encoding="utf-8-sig") as f:
39 >        reader = csv.DictReader(f)
40 >        for row in reader:
41 >            artifact_id = (row.get(artifact_id_col) or "").strip()
42 >            name = (row.get(name_col) or "").strip()
43 >            if artifact_id and name:
44 >                choice_map[artifact_id] = name
45  
46 +    return choice_map
47 +
48 +
49 + # ------------------------------------------------------------
50 + # Main flattener
51 + # ------------------------------------------------------------
52  
53   class RelativityAuditFlattenerXml:
54      """
# Line 22 | Line 56 | class RelativityAuditFlattenerXml:
56      each <field> modification becomes its own row, with the original
57      columns (Name through Request Origination) repeated.
58  
59 <    XML schema (from your inspector):
60 <      - Root: <auditElement>
27 <      - Repeated per-field nodes: <field id="..." type="..." name="..." formatstring="...">
28 <      - Inside <field>:
29 <          <setChoice>...</setChoice>
30 <          <unSetChoice>...</unSetChoice>
31 <          <unsetChoice>...</unsetChoice>
32 <          <oldValue>...</oldValue>
33 <          <newValue>...</newValue>
59 >    For choice fields, setChoice/unSetChoice/unsetChoice values are
60 >    artifactIDs; we map them to choice names using a choice_map dict.
61      """
62  
63      # The base columns we want to carry through from the CSV
# Line 50 | Line 77 | class RelativityAuditFlattenerXml:
77      DETAIL_COLUMNS = [
78          "Field Name",   # from <field name="...">
79          "Field Type",   # from <field type="...">
80 <        "unSetChoice",  # combined <unSetChoice> and <unsetChoice>
81 <        "setChoice",    # <setChoice>
82 <        "oldValue",     # <oldValue>
83 <        "newValue",     # <newValue>
80 >        "unSetChoice",  # choice names removed
81 >        "setChoice",    # choice names added
82 >        "oldValue",     # free-text oldValue
83 >        "newValue",     # free-text newValue
84      ]
85  
86 <    def __init__(self, csv_path: str) -> None:
86 >    def __init__(self, csv_path: str, choice_map: Optional[Dict[str, str]] = None) -> None:
87          self.csv_path = csv_path
88          self.rows: List[Dict[str, Any]] = []
89 +        self.choice_map: Dict[str, str] = choice_map or {}
90  
91 <    # ------------------------------------------------------------
91 >    # --------------------------------------------------------
92      # Public API
93 <    # ------------------------------------------------------------
93 >    # --------------------------------------------------------
94  
95      def load_and_flatten(self) -> None:
96          """
# Line 154 | Line 182 | class RelativityAuditFlattenerXml:
182          wb.Close(SaveChanges=True)
183          xlApp.Quit()
184  
185 <    # ------------------------------------------------------------
185 >    # --------------------------------------------------------
186      # Internal helpers
187 <    # ------------------------------------------------------------
187 >    # --------------------------------------------------------
188  
189      def _copy_base_columns(self, src_row: Dict[str, Any]) -> Dict[str, Any]:
190          """
# Line 167 | Line 195 | class RelativityAuditFlattenerXml:
195              out[col] = src_row.get(col, "")
196          return out
197  
198 +    def _map_choice_id_to_name(self, choice_id: str) -> str:
199 +        """
200 +        Convert a choice artifactID to its choice name using self.choice_map.
201 +        If not found, fall back to the original ID string.
202 +        """
203 +        choice_id = (choice_id or "").strip()
204 +        if not choice_id:
205 +            return ""
206 +        return self.choice_map.get(choice_id, choice_id)
207 +
208      def _parse_details_xml(self, xml_str: str) -> List[Dict[str, str]]:
209          """
210          Parse the XML in Details and return a list of dicts with keys:
211            - field_name
212            - field_type
213 <          - unset_choice  (combined unSetChoice/unsetChoice)
214 <          - set_choice
213 >          - unset_choice  (names, combined)
214 >          - set_choice    (names, combined)
215            - old_value
216            - new_value
217          """
# Line 195 | Line 233 | class RelativityAuditFlattenerXml:
233              field_name = attrib.get("name", "")
234              field_type = attrib.get("type", "")
235  
236 <            # Collect choice changes
237 <            set_choices = [
236 >            # Collect choice changes as IDs
237 >            set_choice_ids = [
238                  (child.text or "").strip()
239                  for child in field_elem.findall("setChoice")
240                  if (child.text or "").strip()
241              ]
242  
243 <            # Note: both <unSetChoice> and <unsetChoice> appear in your data
206 <            unset_choices = [
243 >            unset_choice_ids = [
244                  (child.text or "").strip()
245                  for child in field_elem.findall("unSetChoice")
246                  if (child.text or "").strip()
247              ]
248 <            unset_choices += [
248 >            unset_choice_ids += [
249                  (child.text or "").strip()
250                  for child in field_elem.findall("unsetChoice")
251                  if (child.text or "").strip()
252              ]
253  
254 <            unset_choice_str = "; ".join(unset_choices) if unset_choices else ""
255 <            set_choice_str = "; ".join(set_choices) if set_choices else ""
254 >            # Map IDs -> names
255 >            set_choice_names = [self._map_choice_id_to_name(cid) for cid in set_choice_ids]
256 >            unset_choice_names = [self._map_choice_id_to_name(cid) for cid in unset_choice_ids]
257 >
258 >            set_choice_str = "; ".join(name for name in set_choice_names if name)
259 >            unset_choice_str = "; ".join(name for name in unset_choice_names if name)
260  
261              # Gather old/new value tags (for non-choice cases)
262              old_val_node = field_elem.find("oldValue")
# Line 238 | Line 279 | class RelativityAuditFlattenerXml:
279  
280  
281   # ------------------------------------------------------------
282 < # Example usage
282 > # Script entrypoint
283   # ------------------------------------------------------------
284  
285   if __name__ == "__main__":
286 <    input_csv = r"C:\path\to\RelativityAuditReport.csv"
286 >    # TODO: update these three paths for your environment
287 >    audit_csv_path = r"C:\path\to\RelativityAuditReport.csv"
288 >    choice_csv_path = r"C:\path\to\AllWorkspaceChoices.csv"
289      output_excel = r"C:\path\to\RelativityAuditReport_Flattened_XML.xlsx"
290  
291 <    flattener = RelativityAuditFlattenerXml(input_csv)
292 <    flattener.load_and_flatten()
291 >    # 1) Load the choice map (artifactID -> choice name)
292 >    choice_map = load_choice_map(choice_csv_path)
293 >    print(f"Loaded {len(choice_map)} choices from {choice_csv_path}")
294  
295 +    # 2) Flatten the audit CSV, using the choice map
296 +    flattener = RelativityAuditFlattenerXml(audit_csv_path, choice_map=choice_map)
297 +    flattener.load_and_flatten()
298      print(f"Total field-level changes: {flattener.total_field_changes()}")
299  
300 +    # 3) Export to Excel
301      flattener.export_to_excel(output_excel)
302      print(f"Excel report written to: {output_excel}")
255

Diff Legend

Removed lines
+ Added lines
< Changed lines (old)
> Changed lines (new)