def _values_for_path(self): """ Find values located in document at specified path Parameters: DocId - required string indicating which document to look in Path - required string for location of values to be returned Pub - if present (regardless of value) look for values in the latest publishable version of the document Return: XML document node with the following structure: ReportBody ReportName Value* @Loc """ doc_id = self.__opts.get("DocId") if not doc_id: raise Exception("Missing required 'DocId' parameter") path = self.__opts.get("Path") if not doc_id: raise Exception("Missing required 'Path' parameter") table = "query_term_pub" if "Pub" in self.__opts else "query_term" query = Query(table, "value", "node_loc").order("node_loc") query.where(query.Condition("doc_id", Doc(self.session, id=doc_id).id)) query.where(query.Condition("path", path)) body = self.__start_body() for value, loc in query.execute(self.cursor).fetchall(): etree.SubElement(body, "Value", Loc=loc).text = value return body
def _board_member(self): """ Find corresponding person document linked from board member document Parameters: PersonID - required CDR ID for the Person document for which we want to find the corresponding PDQBoardMemeberInfo document Return: XML document node with the following structure: ReportBody ReportName BoardMember """ person_id = self.__opts.get("PersonId") if not person_id: raise Exception("Missing required 'PersonId' parameter") doc = Doc(self.session, id=person_id) query = Query("query_term", "doc_id") query.where("path = '/PDQBoardMemberInfo/BoardMemberName/@cdr:ref'") query.where(query.Condition("int_val", doc.id)) row = query.execute(self.cursor).fetchone() if not row: message = "No board member found for {}".format(person_id) raise Exception(message) body = self.__start_body() member_id = Doc.normalize_id(row.doc_id) etree.SubElement(body, "BoardMember").text = member_id return body
def _patient_summary(self): """ Find corresponding patient version for HP summary Parameters: HPSummary - required CDR ID for the Health Professional for which we want to find the patient verion Return: XML document node with the following structure: ReportBody ReportName PatientSummary """ hp_id = self.__opts.get("HPSummary") if not hp_id: raise Exception("Missing required 'HPSummary' parameter") doc = Doc(self.session, id=hp_id) query = Query("query_term", "doc_id") query.where("path = '/Summary/PatientVersionOf/@cdr:ref'") query.where(query.Condition("int_val", doc.id)) row = query.execute(self.cursor).fetchone() if not row: message = "No patient summary found for {}".format(hp_id) raise Exception(message) body = self.__start_body() patient_id = Doc.normalize_id(row.doc_id) etree.SubElement(body, "PatientSummary").text = patient_id return body
def __init__(self): """Assemble the dictionary of all control value groups.""" groups = {} query = Query("ctl", "grp", "name", "val", "comment") query.where("inactivated IS NULL") for row in query.execute().fetchall(): key = row.grp.lower() group = groups.get(key) if group is None: group = groups[key] = self.Group(row.grp, key) value = self.Value(row.name, row.val, row.comment) group.values[value.key] = value self.groups = groups
def _dated_actions(self): """ Find the to-do list for documents of a particular type Parameters: DocType - required string naming the document type for which documents should be included Return: XML document node with the following structure: ReportBody ReportName ReportRow* DocId DocTitle """ doctype = self.__opts.get("DocType") if not doctype: raise Exception("Missing required 'DocType' parameter") path = "/{}/DatedAction/ActionDate".format(doctype) query = Query("document d", "d.id", "d.title").unique() query.join("query_term a", "a.doc_id = d.id") query.where(query.Condition("a.path", path)) rows = query.execute(self.cursor).fetchall() body = self.__start_body() for id, title in rows: wrapper = etree.SubElement(body, "ReportRow") etree.SubElement(wrapper, "DocId").text = Doc.normalize_id(id) etree.SubElement(wrapper, "DocTitle").text = title result = Doc(self.session, id=id).filter("name:Dated Actions") wrapper.append(result.result_tree.getroot()) return body
def _translated_summary(self): """ Find corresponding translation of English summary Parameters: EnglishSummary - required CDR ID for the document for which we want to find the Spanish translation Return: XML document node with the following structure: ReportBody ReportName TranslatedSummary """ english_id = self.__opts.get("EnglishSummary") if not english_id: raise Exception("Missing required 'EnglishSummary' parameter") doc = Doc(self.session, id=english_id) query = Query("query_term", "doc_id") query.where("path = '/Summary/TranslationOf/@cdr:ref'") query.where(query.Condition("int_val", doc.id)) row = query.execute(self.cursor).fetchone() if not row: message = "No translated summary found for {}".format(english_id) raise Exception(message) body = self.__start_body() spanish_id = Doc.normalize_id(row.doc_id) etree.SubElement(body, "TranslatedSummary").text = spanish_id return body
def get_rules(cls, session): """ Find the available query term rules Used by the user interface for managing search path definitions. See notes above in the documentation for the `QueryTermDef` class on the custom rule mechanism, which AFAIK has never been used. Required positional argument: session - reference to object for current login Called by: cdr.listQueryTermRules() client XML wrapper command CdrListQueryTermRules """ session.log("QueryTermDef.get_rules()") query = Query("query_term_rule", "name").order("name") return [row.name for row in query.execute(session.cursor).fetchall()]
def subsystem(self): """ Reference to the `Job.Subsystem` object controlling this job """ if not hasattr(self, "_subsystem"): self._subsystem = None if self.system: if self.system.root is None: doc = self.system args = doc.cdr_id, doc.version, doc.title doc = "{}V{} ({})".format(*args) message = "{} can't be parsed".format(doc) raise Exception(message) name = self.__opts.get("subsystem") if not name and self.id: query = Query("pub_proc", "pub_subset") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception("Job {} not found".format(self.id)) name = row.pub_subset if name: path = "SystemSubset/SubsetName" for node in self.system.root.findall(path): if Doc.get_text(node) == name: parent = node.getparent() self._subsystem = Job.Subsystem(parent, name) break return self._subsystem
def parms(self): """ Dictionary of parameters for the job Default values from the control document. Some may be overridden for this job. Have to strip unwanted space from the values; see https://sourceforge.net/p/adodbapi/bugs/27/ """ if not hasattr(self, "_parms"): if self.id: query = Query("pub_proc_parm", "parm_name", "parm_value") query.where(query.Condition("pub_proc", self.id)) self._parms = {} for name, value in query.execute(self.cursor).fetchall(): if value is not None: value = value.strip() # bug in adodbapi self._parms[name] = value else: if not self.subsystem: raise Exception("Missing publishing subsystem") requested = self.__opts.get("parms") or {} defined = self.subsystem.parms.copy() undefined = set(requested) - set(defined) if undefined: messages = "Paramater(s) {} undefined" #raise Exception(messages.format(", ".join(undefined))) raise Exception(messages.format(undefined)) defined.update(requested) self.session.logger.info("job parms: %r", defined) self._parms = defined return self._parms
def docs(self): """ List of documents requested or published for this job For a job which is being created, this will be populated from the `docs` argument passed into the constructor. For a job which is already in the database and which has been published, the list will be pulled from the `pub_proc_doc` table. For a job which is in the database, queued but not yet run, the list will have the documents explicitly requested at job creation time, but will not yet have the documents which will be picked up by the control document's query logic for this job type. It might seem redundant to create new Doc objects for an unsaved job's documents, but in most cases the Doc objects passed to the constructor will only have document IDs, and no version information. We can't really get the date/time cutoff used to pick the right version without creating a new object. """ if not hasattr(self, "_docs"): docs = [] if self.id: query = Query("pub_proc_doc", "doc_id", "doc_version") query.where(query.Condition("pub_proc", self.id)) for doc_id, version in query.execute(self.cursor).fetchall(): docs.append(Doc(self.session, id=doc_id, version=version)) else: cutoff = self.parms.get("MaxDocUpdatedDate") if not cutoff or cutoff == "JobStartDateTime": cutoff = self.started opts = dict(before=cutoff) for requested in self.__opts.get("docs", []): opts["id"] = requested.id if self.force: opts["version"] = "last" elif requested.version: if not (self.permissive or requested.publishable): args = requested.cdr_id, requested.version message = "{}V{} is not publishable".format(*args) raise Exception(message) opts["version"] = requested.version else: opts["version"] = "lastp" try: doc = Doc(self.session, **opts) if not self.force and doc.active_status != "A": raise Exception("{} is blocked".format(doc.id)) docs.append(doc) except Exception as e: raise Exception("{}: {}".format(requested.cdr_id, e)) self._docs = docs return self._docs
def output_dir(self): """ Destination location for the exported documents """ if not hasattr(self, "_output_dir"): if self.id: query = Query("pub_proc", "output_dir") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception("Job {} not found".format(self.id)) self._output_dir = row.output_dir.replace("\\", "/") else: self._output_dir = None return self._output_dir
def completed(self): """ Date/time when the job finished """ if not hasattr(self, "_completed"): if self.id: query = Query("pub_proc", "completed") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception("Job {} not found".format(self.id)) self._completed = row.completed else: self._completed = None if isinstance(self._completed, datetime.datetime): self._completed = self._completed.replace(microsecond=0) return self._completed
def started(self): """ When the job was created (so a bit of a misnomer) """ if not hasattr(self, "_started"): if self.id: query = Query("pub_proc", "started") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception("Job {} not found".format(self.id)) self._started = row.started else: self._started = datetime.datetime.now() if isinstance(self._started, datetime.datetime): self._started = self._started.replace(microsecond=0) return self._started
def no_output(self): """ Flag indicating that document output won't be written to disk """ if not hasattr(self, "_no_output"): if "no_output" in self.__opts: self._no_output = self.__opts["no_output"] elif self.id: query = Query("pub_proc", "no_output") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception("Job {} not found".format(self.id)) self._no_output = row.no_output == "Y" else: self._no_output = False return self._no_output
def rule_id(self): """ Primary key of row in the `query_term_rule` table (or None) See notes above about the custom rule mechanism, which AFAIK has never been used. """ if not hasattr(self, "_rule_id"): if not self.rule: self._rule_id = None else: query = Query("query_term_rule", "id") query.where(query.Condition("name", self.rule)) row = query.execute(self.session.cursor).fetchone() if not row: raise Exception(f"Unknown query term rule: {self.rule}") self._rule_id = row.id return self._rule_id
def get_definitions(cls, session): """ Fetch the list of CDR query term definitions Required positional argument: session - reference to object for current login Called by: cdr.listQueryTermDefs() client XML wrapper command CdrListQueryTermDefs Return: sequence of `QueryTermDef` objects """ session.log("QueryTermDef.get_definitions()") query = Query("query_term_def d", "d.path", "r.name") query.outer("query_term_rule r", "r.id = d.term_rule") query.order("d.path", "r.name") definitions = [] for row in query.execute(session.cursor).fetchall(): definitions.append(QueryTermDef(session, row.path, row.name)) return definitions
def email(self): """ String for the email address(es) to which processing notifications go """ if not hasattr(self, "_email"): if "email" in self.__opts: self._email = self.__opts["email"] if isinstance(self._email, (list, tuple)): self._email = ",".join(self._email) elif self.id: query = Query("pub_proc", "email") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception(f"Job {self.id} not found") self._email = row.email else: self._email = None return self._email
def add(self): """ Store the new query term definition Called by: cdr.addQueryTermDef() client XML wrapper command CdrAddQueryTermDef """ self.session.log(f"QueryTermDef.add({self.path!r}, {self.rule!r})") if not self.session.can_do("ADD QUERY TERM DEF"): message = "User not authorized to add query term definitions" raise Exception(message) if not self.path: raise Exception("Missing required path") query = Query("query_term_def", "COUNT(*) AS n") query.where(query.Condition("path", self.path)) if query.execute(self.session.cursor).fetchone().n > 0: raise Exception("Duplicate query term definition") names = "path, term_rule" values = self.path, self.rule_id insert = f"INSERT INTO query_term_def ({names}) VALUES (?, ?)" self.session.cursor.execute(insert, values) self.session.conn.commit()
def query(self): """ Assemble the database query object for performing the search """ # Use cached `Query` object if we've already done this. if hasattr(self, "_query"): return self._query # Create a new `Query` object and apply any doctype filters. query = Query("document d", "d.id").order("d.title") if self.doctypes: query.join("doc_type t", "t.id = d.doc_type") if len(self.doctypes) == 1: query.where(query.Condition("t.name", self.doctypes[0])) else: query.where(query.Condition("t.name", self.doctypes, "IN")) # Apply the conditions for each of the `Test` objects. n = 1 for test in self.tests: # If the `Test` object specifies a column use the `query_term` # table. if test.column: alias = f"qt{n:d}" n += 1 query.join("query_term " + alias, alias + ".doc_id = d.id") query.where(query.Condition(alias + ".path", test.path)) column = f"{alias}.{test.column}" # Otherwise, the test is looking for matching title strings. else: column = "d.title" # Construct and add a new `Condition` object to the query. query.where(query.Condition(column, test.value, test.operator)) # If the caller doesn't want all the matching documents, apply the # limit requested. if self.limit: query.limit(self.limit) # Cache and return the `Query` object. self._query = query return query
def _glossary_term_names(self): """ Find GlossaryTermName documents linked to a GlossaryTermConcept doc Parameters: ConceptID - required CDR ID for the master concept document Return: XML document node with the following structure: ReportBody ReportName GlossaryTermName* @ref """ concept_id = self.__opts.get("ConceptId") if not concept_id: raise Exception("Missing required 'ConceptId' parameter") path = "/GlossaryTermName/GlossaryTermConcept/@cdr:ref" doc = Doc(self.session, id=concept_id) query = Query("document d", "d.id", "d.title") query.join("query_term n", "n.doc_id = d.id") query.where(query.Condition("n.path", path)) query.where(query.Condition("n.int_val", doc.id)) body = self.__start_body() for id, title in query.execute(self.cursor).fetchall(): cdr_id = Doc.normalize_id(id) etree.SubElement(body, "GlossaryTermName", ref=cdr_id).text = title return body
def _inactive_checked_out_documents(self): """ Report on locked documents which haven't seen any action recently Parameters: InactivityLength - required string for how long a document can be active before showing up on the report in the form YYYY-MM-DD Return: XML document node with the following structure: ReportBody ReportName ReportRow* DocId DocType CheckedOutTo WhenCheckedOut LastActivity ActionType ActionWhen """ deltas = self.__opts.get("InactivityLength") if not deltas: raise Exception("Missing required 'InactivityLength' parameter") try: years, months, days = [int(digits) for digits in deltas.split("-")] except: message = "InactivityLength parameter must be in YYYY-MM-DD format" raise Exception(message) delta = relativedelta(years=years, months=months, days=days) cutoff = (datetime.now() - delta).replace(microsecond=0) subquery = Query("audit_trail", "MAX(dt)") subquery.where("document = a.document") fields = "d.id", "t.name", "c.dt_out", "u.name", "w.name", "a.dt" query = Query("document d", *fields) query.join("doc_type t", "t.id = d.doc_type") query.join("checkout c", "c.id = d.id") query.join("open_usr u", "u.id = c.usr") query.join("audit_trail a", "a.document = d.id") query.join("action w", "w.id = a.action") query.where("c.dt_in IS NULL") query.where(query.Condition("c.dt_out", cutoff, "<")) query.where(query.Condition("a.dt", cutoff, "<")) query.where(query.Condition("a.dt", subquery)) rows = query.order("c.id").execute(self.cursor).fetchall() body = self.__start_body() for id, doctype, locked, locker, what, when in rows: wrapper = etree.SubElement(body, "ReportRow") etree.SubElement(wrapper, "DocId").text = Doc.normalize_id(id) etree.SubElement(wrapper, "DocType").text = doctype etree.SubElement(wrapper, "CheckedOutTo").text = locker etree.SubElement(wrapper, "WhenCheckedOut").text = str(locked) last_activity = etree.SubElement(wrapper, "LastActivity") etree.SubElement(last_activity, "ActionType").text = what etree.SubElement(last_activity, "ActionWhen").text = str(when) return body
def system(self): """ Reference to `Doc` object for job's publishing control document """ if not hasattr(self, "_system"): opts = dict(id=None, before=self.started) if self.id: query = Query("pub_proc", "pub_system") query.where(query.Condition("id", self.id)) row = query.execute(self.cursor).fetchone() if not row: raise Exception("Job {} not found".format(self.id)) opts["id"] = row.pub_system else: name = self.__opts.get("system") if name: query = Query("document d", "d.id") query.join("doc_type t", "t.id = d.doc_type") query.where("t.name = 'PublishingSystem'") query.where(query.Condition("d.title", name)) rows = query.execute(self.cursor).fetchall() if len(rows) > 1: raise Exception(f"multiple {name} docs") if not rows: message = "Publishing system {!r} not found" raise Exception(message.format(name)) row = rows[0] opts["id"] = row.id opts["title"] = name if opts["id"]: self._system = Doc(self.session, **opts) else: self._system = None return self._system
def _locked_documents(self): """ Report on documents locked by specified user Parameters: UserId - required string identifying a user account; this is the value often referred to as the 'name' rather than the integer primary key for the row in the usr table Return: XML document node with the following structure: ReportBody ReportName ReportRow* DocId DocType WhenCheckedOut """ user_name = self.__opts.get("UserId") if not user_name: raise Exception("Missing required 'UserId' parameter") query = Query("document d", "d.id", "t.name", "c.dt_out") query.join("doc_type t", "t.id = d.doc_type") query.join("checkout c", "c.id = d.id") query.join("open_usr u", "u.id = c.usr") query.where("c.dt_in IS NULL") query.where(query.Condition("u.name", user_name)) rows = query.order("c.id").execute(self.cursor).fetchall() body = self.__start_body() for id, doctype, locked in rows: wrapper = etree.SubElement(body, "ReportRow") etree.SubElement(wrapper, "DocId").text = Doc.normalize_id(id) etree.SubElement(wrapper, "DocType").text = doctype etree.SubElement(wrapper, "WhenCheckedOut").text = str(locked) return body
def _menu_term_tree(self): """ Report on the menu term hierarchy Parameters: MenuType - required string restricting report to a single menu type Return: XML document node with the following structure: ReportBody ReportName MenuItem* TermId TermName MenuType MenuStatus DisplayName? ParentId? SortString """ menutype = self.__opts.get("MenuType", "%") operand = "LIKE" if "%" in menutype else "=" fields = ("n.doc_id AS id", "n.value AS name", "t.value AS type", "s.value AS status", "d.value AS display", "p.int_val AS parent", "k.value AS sort_key") i_path = "/Term/MenuInformation/MenuItem" n_path = "/Term/PreferredName" t_path = "{}/MenuType".format(i_path) s_path = "{}/MenuStatus".format(i_path) d_path = "{}/DisplayName".format(i_path) p_path = "{}/MenuParent/@cdr:ref".format(i_path) k_path = "{}/@SortOrder".format(i_path) query = Query("query_term n", *fields).unique().order("n.doc_id") query.join("query_term t", "t.doc_id = n.doc_id") query.join("query_term s", "s.doc_id = t.doc_id", "LEFT(s.node_loc, 8) = LEFT(t.node_loc, 8)") query.outer("query_term d", "d.doc_id = t.doc_id", "d.path = '{}'".format(d_path), "LEFT(d.node_loc, 8) = LEFT(t.node_loc, 8)") query.outer("query_term p", "p.doc_id = t.doc_id", "p.path = '{}'".format(p_path), "LEFT(p.node_loc, 8) = LEFT(t.node_loc, 8)") query.outer("query_term k", "k.doc_id = t.doc_id", "k.path = '{}'".format(k_path), "LEFT(k.node_loc, 8) = LEFT(t.node_loc, 8)") query.where("n.path = '{}'".format(n_path)) query.where("t.path = '{}'".format(t_path)) query.where("s.path = '{}'".format(s_path)) query.where(query.Condition("t.value", menutype, operand)) query.where(query.Condition("s.value", "Offline", "<>")) body = self.__start_body() for row in query.execute(self.cursor).fetchall(): sort_key = row.sort_key if row.sort_key is not None else row.name wrapper = etree.SubElement(body, "MenuItem") etree.SubElement(wrapper, "TermId").text = str(row.id) etree.SubElement(wrapper, "TermName").text = row.name etree.SubElement(wrapper, "MenuType").text = row.type etree.SubElement(wrapper, "MenuStatus").text = row.status if row.display is not None: etree.SubElement(wrapper, "DisplayName").text = row.display if row.parent is not None: etree.SubElement(wrapper, "ParentId").text = str(row.parent) etree.SubElement(wrapper, "SortString").text = sort_key return body
def lockers(self): """Get sequence of id/name pairs for users with locked documents.""" if not hasattr(self, "_lockers"): fields = "COUNT(*) AS n", "u.id", "u.name", "u.fullname" query = Query("usr u", *fields) query.join("checkout c", "c.usr = u.id") query.join("document d", "d.id = c.id") query.group(*fields[1:]) query.where("c.dt_in IS NULL") users = [] for row in query.execute(self.cursor): name = row.fullname or row.name display = f"{name} ({row.n} locks)" users.append((name.lower(), row.id, display)) self._lockers = [(uid, label) for key, uid, label in sorted(users)] return self._lockers
def _genetics_syndromes(self): """ Find Term documents used to represent genetics syndromes Parameters: TitlePattern - optional string narrowing report to syndrome names which match the pattern Return: XML document node with the following structure: ReportBody ReportName ReportRow* DocId DocTitle """ pattern = self.__opts.get("TitlePattern") query = Query("document d", "d.id", "d.title") query.unique().order("d.title") query.join("query_term t", "t.doc_id = d.id") query.join("query_term s", "s.doc_id = t.doc_id", "LEFT(s.node_loc, 8) = LEFT(t.node_loc, 8)") query.where("t.path = '/Term/MenuInformation/MenuItem/MenuType'") query.where("s.path = '/Term/MenuInformation/MenuItem/MenuStatus'") query.where("s.value = 'Online'") query.where("t.value = 'Genetics Professionals--GeneticSyndrome'") if pattern: query.where(query.Condition("d.title", pattern, "LIKE")) body = self.__start_body() for id, title in query.execute(self.cursor).fetchall(): wrapper = etree.SubElement(body, "ReportRow") etree.SubElement(wrapper, "DocId").text = Doc.normalize_id(id) etree.SubElement(wrapper, "DocTitle").text = title return body
def __find_pending_job(self): """ See if we've already got a job of this type in progress Used to prevent having two or more of the same publishing job type in flight at the same time. Return: True if there's already another job of this type in progress """ query = Query("pub_proc", "id").limit(1) query.where(query.Condition("pub_system", self.system.id)) query.where(query.Condition("pub_subset", self.subsystem.name)) query.where("status NOT IN ('Success', 'Failure')") row = query.execute(self.cursor).fetchone() return row.id if row else None
def _term_sets(self): """ Collect named sets of CDR terms Parameters: SetType - optional string restricting report to term sets whose type name matches the pattern passed (any wildcards in the pattern must be included, the report code does not wrap the string in a pair of wildcards); as of this writing, this parameter is of limited use, as all of the existing term sets have the set type of "diagnosis macro" Return: XML document node with the following structure: ReportBody ReportName TermSet* Name Members [string of CDR term IDs] """ query = Query("doc_version v", "v.id", "MAX(v.num) AS num") query.join("query_term_pub t", "t.doc_id = v.id") query.where("t.path = '/TermSet/TermSetType'") query.where("v.publishable = 'Y'") query.group("v.id") pattern = self.__opts.get("SetType") if pattern: query.where(query.Condition("t.value", pattern, "LIKE")) body = self.__start_body() for id, num in query.execute(self.cursor).fetchall(): doc = Doc(self.session, id=id, version=num) result = doc.filter("name:Get TermSet Name and Members") body.append(result.result_tree.getroot()) return body
def build_tables(self): """Show the documents the user has locked.""" fields = "c.dt_out", "t.name", "d.id", "d.title" query = Query("usr u", *fields).order(*fields[:3]) query.join("checkout c", "c.usr = u.id") query.join("document d", "d.id = c.id") query.join("doc_type t", "t.id = d.doc_type") query.where("c.dt_in IS NULL") query.where(query.Condition("u.id", self.user.id)) rows = [] for dt_out, doc_type, doc_id, title in query.execute(self.cursor): doc_id = Reporter.Cell(doc_id, center=True) rows.append([str(dt_out)[:19], doc_type, doc_id, title]) caption = f"Checked out by {self.user.fullname or self.user.name}" return Reporter.Table(rows, caption=caption, columns=self.COLUMNS)
#!/usr/bin/env python """Get one XML document from pub_proc_cg to stdout. If your document has non-ascii characters in it it may not work well to try and pipe the output to a tool which expects ascii to be coming from stdout. Consider instead using the --save option and loading the encoded document directly into your tool. """ from argparse import ArgumentParser from cdrapi.db import Query parser = ArgumentParser() parser.add_argument("id", type=int) parser.add_argument("--tier") parser.add_argument("--save", action="store_true") opts = parser.parse_args() query = Query("pub_proc_cg", "xml") query.where(query.Condition("id", opts.id)) row = query.execute().fetchone() if not row: raise Exception(f"CDR{opts.id:d} not found in pub_proc_cg") if opts.save: with open(f"pub_proc_cg-{opts.id:d}.xml", "wb") as fp: fp.write(row.xml.encode("utf-8")) else: print(row.xml)