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)