def replace(self): """Promote the selected version, log the action, and show the form.""" if not self.session.can_do("MODIFY DOCUMENT", self.doc.doctype.name): self.bail("Not authorized") if not self.version: self.bail("No version specified") comment = f"Replacing CWD with version {self.version:d}" doc = Doc(self.session, id=self.doc.id, version=self.version) doc.check_out(comment=comment) pub_ver = self.make_version_publishable if self.comment: comment = f"{comment}: {self.comment}" opts = dict( version=self.create_version, publishable=pub_ver, val_types=("schema", "links") if pub_ver else None, comment=comment, reason=comment, unlock=True, ) doc.save(**opts) path = f"{self.session.tier.logdir}/CWDReplacements.log" try: with open(path, "a", encoding="utf-8") as fp: fp.write(f"{self.message}\n") except IOError as e: self.bail(f"Error writing to {path}: {e}") self.show_form()
def doc(self): """Prepare a `cdrapi.Doc` object for saving in the CDR""" # We may have already done the work and cached the object. if not hasattr(self, "_doc"): # If we're updating an existing Citation doc, fetch and modify it. if self.control.cdrid: cdrid = self.control.cdrid doc = Doc(self.control.session, id=cdrid) doc.check_out() root = doc.root old_node = root.find("PubmedArticle") if old_node is None: raise Exception(f"{cdrid} is not a PubMed article") root.replace(old_node, self.pubmed_article) doc.xml = etree.tostring(root) # Otherwise, build up a new document and insert NLM's info. else: pmid = self.control.pmid cdrid = self.lookup(pmid) if cdrid: raise Exception(f"PMID {pmid} already imported as {cdrid}") root = etree.Element("Citation") details = etree.SubElement(root, "VerificationDetails") etree.SubElement(details, "Verified").text = "Yes" etree.SubElement(details, "VerifiedIn").text = "PubMed" root.append(self.pubmed_article) opts = dict(xml=etree.tostring(root), doctype="Citation") doc = Doc(self.control.session, **opts) self._doc = doc return self._doc
def expire(self): """ Update data partner's CDR document to reflect deactivation. """ doc = Doc(self.control.session, id=self.org_id) root = doc.root node = root.find("LicenseeInformation/LicenseeStatus") if node.text == "Test-inactive": self.logger.warning("CDR%s already deactivated", self.org_id) return self.logger.info("Marking CDR%s as expired", self.org_id) node.text = "Test-inactive" today = str(datetime.date.today()) node = root.find("DateLastModified") if node is None: node = etree.SubElement(root, "DateLastModified") node.text = today node = root.find("LicenseeInformation/LicenseeStatusDates") if node is None: message = "CDR%s missing LicenseeStatusDates" self.logger.warning(message, self.org_id) else: child = node.find("TestInactivation") if child is None: child = etree.Element("TestInactivation") sibling = node.find("TestActivation") if sibling is not None: sibling.addnext(child) else: node.insert(0, child) if child.text is None or not child.text.strip(): child.text = today if not self.control.test: comment = "Marking account as inactive" doc.check_out(force=True, comment=comment) opts = dict( version=True, reason=comment, comment=comment, unlock=True, ) doc.save(**opts)
help="make version publishable", action="store_true") parser.add_argument("--force", "-f", help="force checkout", action="store_true") opts = parser.parse_args() if opts.session: session = Session(opts.session, tier=opts.tier) else: session = Session.create_session(opts.user, tier=opts.tier) print(session) with open(opts.xml, "rb") as fp: xml = fp.read() doc = Doc(session, id=opts.id, doctype=opts.doctype, xml=xml) if opts.id: doc.check_out(force=opts.force) version = opts.version or opts.publishable val_types = ["schema", "links"] if opts.publishable else None save_opts = dict( version=version, publishable=opts.publishable, val_types=val_types, comment=opts.comment, unlock=True, ) doc.save(**save_opts) print(f"saved {doc.cdr_id} ({doc.title})") if not opts.session: session.logout()
class Control(Controller): """Access to the current CDR login session and page-building tools.""" SUBTITLE = "Post CDR Schema" LOGNAME = "post-schema" ACTIONS = ( ("add", "Add New Schema"), ("replace", "Replace Existing Schema"), ) def populate_form(self, page): """Prompt for the file and an optional comment. Checking for processing logs triggers the schema save if a schema file has been posted, and in that case the logs are displayed below the form. Pass: page - HTMLPage on which we place the fields """ fieldset = page.fieldset("Schema") fieldset.append(page.file_field("file", label="Schema File")) fieldset.append(page.text_field("comment")) page.form.append(fieldset) fieldset = page.fieldset("Action") checked = False for value, label in self.ACTIONS: opts = dict(value=value, label=label, checked=checked) fieldset.append(page.radio_button("action", **opts)) checked = True page.form.append(fieldset) if self.logs is not None: fieldset = page.fieldset("Processing Logs") fieldset.append(self.logs) page.form.append(fieldset) page.add_css("fieldset { width: 600px } #comment { width: 450px }") page.add_css("pre { color: green }") page.form.set("enctype", "multipart/form-data") def show_report(self): """Cycle back to the form.""" self.show_form() @property def action(self): """Are we replacing an existing schema or adding a new one?""" return self.fields.getvalue("action") @property def buttons(self): """Customize the action buttons on the banner bar.""" return self.SUBMIT, self.DEVMENU, self.ADMINMENU, self.LOG_OUT @property def comment(self): """Override the default comment as appropriate.""" if not hasattr(self, "_comment"): self._comment = self.fields.getvalue("comment") if not self._comment: verb = "Replacing" if self.document.id else "Adding" self._comment = f"{verb} schema using admin interface" return self._comment @property def document(self): """Uploaded schema document to be posted.""" if not hasattr(self, "_document"): self._document = None if self.schema_title and self.file_bytes: title = self.schema_title xml = self.file_bytes opts = dict(xml=xml, doctype="schema") query = self.Query("document d", "d.id") query.join("doc_type t", "t.id = d.doc_type") query.where("t.name = 'schema'") query.where(query.Condition("d.title", title)) rows = query.execute(self.cursor).fetchall() if rows: if self.action == "add": raise Exception(f"Schema {title} already exists") if len(rows) > 1: raise Exception(f"Multiple schemas match {title}") opts["id"] = rows[0].id else: if self.action != "add": raise Exception(f"Schema document {title} not found") self._document = Doc(self.session, **opts) if rows: self._document.check_out(comment="locking for update") return self._document @property def file_bytes(self): """UTF-8 serialization of the document to be posted.""" if not hasattr(self, "_file_bytes"): self._file_bytes = None if self.file_field is not None: if self.file_field.file: segments = [] while True: more_bytes = self.file_field.file.read() if not more_bytes: break segments.append(more_bytes) else: segments = [self.file_field.value] self._file_bytes = b"".join(segments) return self._file_bytes @property def file_field(self): """CGI field for the uploaded file, if any.""" if not hasattr(self, "_file_field"): self._file_field = None if "file" in list(self.fields.keys()): self._file_field = self.fields["file"] return self._file_field @property def logs(self): """Log output describing the outcome of a post action.""" if not hasattr(self, "_logs"): self._logs = None if self.document: if not self.session.can_do("MODIFY DOCUMENT", "schema"): error = "Account not authorized for posting schemas." self.bail(error) self.document.save(**self.opts) message = "\n\n".join([ f"Saved {self.document.cdr_id}.", self.__check_dtds().strip(), self.__refresh_manifest().strip(), "Schema posted successfully.", f"Elapsed: {self.elapsed}", ]) self._logs = self.HTMLPage.B.PRE(message) return self._logs @property def opts(self): """Options passed to the `Doc.save()` method.""" if not hasattr(self, "_opts"): self._opts = dict( version=True, unlock=True, comment=self.comment, reason=self.comment, title=self.schema_title, ) return self._opts @property def schema_title(self): """Name of the uploaded file without the full path.""" if not hasattr(self, "_filename"): self._title = None if self.file_field is not None: self._title = basename(self.file_field.filename) self.logger.info("filename for schema is %r", self._title) return self._title def __check_dtds(self): """Regenerate the DTDs to reflect the new/updated schema. Return: string to append to the processing log display """ cmd = r"python d:\cdr\build\CheckDtds.py" result = run_command(cmd, merge_output=True) if result.returncode: raise Exception(f"DTD check failure: {result.stdout}") self.logger.info("DTDs updated") return "Running CheckDtds.py ...\n" + result.stdout def __refresh_manifest(self): """Rebuild the client manifest file. Return: string to append to the processing log display """ cmd = r"python d:\cdr\build\RefreshManifest.py" result = run_command(cmd, merge_output=True) if result.returncode: output = result.stdout raise Exception(f"Manifest refresh failure: {output}") self.logger.info("Manifest updated") return "Running RefreshManifest.py ...\n" + result.stdout