1#!/usr/bin/env python3
2
3"""Read and write Workorder files."""
4
5import logging
6
7from chart.common.xml import Element
8from chart.common.xml import SubElement
9import chart.alg.settings
10from chart.common.xml import load_xml
11from chart.common.xml import write_xml
12from chart.common.xml import datetime_to_xml
13from chart.common.xml import parsechildstr
14from chart.backend.activity import Activity
15from chart.backend.job import Job
16
17logger = logging.getLogger()
18
19pretty_print = True
20
21ELEM_WORK_ORDER = 'work-order'
22ELEM_ID = 'id'
23ELEM_SCID = 'scid'
24ELEM_ACTIVITY = 'activity'
25ELEM_ORBIT = 'orbit'
26ELEM_CATEGORY = 'category'
27ELEM_FILENAME = 'filename'
28ELEM_SENSING_START = 'sensing-start'
29ELEM_SENSING_STOP = 'sensing-stop'
30ELEM_JOB = 'job'
31ELEM_TABLENAME = 'tablename'
32
33
34class WorkOrder:
35 """Read and write Workorder files.
36 The filename is always settings.WORKORDER_FILENAME ('workorder.xml')."""
37
38 def __init__(self,
39 filename=None,
40 mode='r',
41 activity=None,
42 category=None):
43 """
44 Args:
45 `filename` (Path): File name to use.
46 `mode` (str:'r'|'w): read or write a work order.
47 `activity` (Activity): In write mode only, specify the activity.
48 `category` (str): In write mode only state the category for this groups of jobs.
49 """
50 if filename is None:
51 # note, don't use WORKORDER_FILENAME in the constructor arguments
52 # due to circular dependancy
53 filename = chart.alg.settings.WORKORDER_FILENAME
54
55 self.mode = mode
56 self.filename = filename
57
58 if mode == 'w':
59 self.elem = Element(ELEM_WORK_ORDER)
60 SubElement(self.elem, ELEM_CATEGORY).text = category
61 if activity is not None:
62 SubElement(self.elem, ELEM_ACTIVITY).text = activity.name
63
64 self.activity = activity
65
66 elif mode == 'r':
67 if activity is not None or category is not None:
68 raise ValueError('Cannot specify activity name or category when reading '
69 'a work order')
70
71 self.elem = load_xml(filename)
72 self.activity = Activity(parsechildstr(self.elem, ELEM_ACTIVITY))
73
74 else:
75 raise ValueError("Mode must be either 'r' (default) or 'w'")
76
77 def add_job(self, job):
78 """Insert a new `job` to a writable work order file."""
79 if self.mode == 'r':
80 raise ValueError('Cannot add jobs to a read-only workorder object')
81
82 job_elem = SubElement(self.elem, ELEM_JOB)
83
84 if job.job_id is not None:
85 SubElement(job_elem, ELEM_ID).text = str(job.job_id)
86
87 if job.filename is not None:
88 SubElement(job_elem, ELEM_FILENAME).text = str(job.filename)
89
90 if job.sid is not None:
91 job.sid.to_xml(job_elem)
92
93 if job.orbit is not None:
94 SubElement(job_elem, ELEM_ORBIT).text = str(job.orbit)
95
96 if job.sensing_start is not None:
97 SubElement(job_elem, ELEM_SENSING_START).text = datetime_to_xml(
98 job.sensing_start, include_us=True)
99
100 if job.sensing_stop is not None:
101 SubElement(job_elem, ELEM_SENSING_STOP).text = datetime_to_xml(
102 job.sensing_stop, include_us=True)
103
104 if job.tablename is not None:
105 SubElement(job_elem, ELEM_TABLENAME).text = job.tablename
106
107 def write(self):
108 """Record instance to an XML file."""
109 write_xml(self.elem, self.filename, pretty_print)
110
111 def read_jobs(self):
112 """Generator function to yield all jobs in a readable work order file."""
113 for job_elem in self.elem.findall(ELEM_JOB):
114 result = Job.from_xml(job_elem)
115 result.activity = self.activity
116 result.category = self.category
117 logger.info('Begin processing job {jobid}'.format(jobid=result.job_id))
118 yield result
119
120 def __iter__(self):
121 return self.read_jobs()
122
123 @property
124 def category(self):
125 """Return category of this work order."""
126 return parsechildstr(self.elem, ELEM_CATEGORY)