Ejemplo n.º 1
0
    def columns(self):
        """
        Create a sequence of column definitions for the output report.

        Number and types of columns depend on config parms.

        Return:
            Sequence of column definitions to add to object.
        """

        return (Reporter.Column("Doc ID", width="70px"),
                Reporter.Column("Doc Title", width="200px"),
                Reporter.Column("Match", width="100px"),
                Reporter.Column("Context", width="200px"),
                Reporter.Column("Standard Wording?", width="50px"))
Ejemplo n.º 2
0
    def excel_cols(self):
        """Column names wrapped in `Reporter.Cell` objects.

        This lets us have some control over wrapping and column width.
        """

        if not hasattr(self, "_excel_cols"):
            self._excel_cols = []
            for col in self.cols:
                self._excel_cols.append(Reporter.Column(col, width="250px"))
        return self._excel_cols
Ejemplo n.º 3
0
 def build_tables(self):
     parms = {SESSION: self.session, REQUEST: "View", "full": "full"}
     ids = []
     rows = []
     for doc in FilterSet.get_filters(self.session):
         parms[DOCID] = doc.cdr_id
         url = f"EditFilter.py?{urlencode(parms)}"
         id_cell = Reporter.Cell(doc.cdr_id, href=url)
         ids.append((doc.id, id_cell, doc.title))
         rows.append((id_cell, doc.title))
     columns = (
         Reporter.Column("CDR ID", classes="id-col"),
         Reporter.Column("Filter Title", classes="title-col"),
     )
     caption = f"{len(rows):d} CDR Filters (Sorted By Title)"
     opts = dict(caption=caption, columns=columns, id="titlesort")
     opts["logger"] = self.logger
     tables = [Reporter.Table(rows, **opts)]
     rows = [(deepcopy(row[1]), row[2]) for row in sorted(ids)]
     opts["caption"] = f"{len(rows):d} CDR Filters (Sorted By CDR ID)"
     opts["id"] = "idsort"
     tables.append(Reporter.Table(rows, **opts))
     return tables
Ejemplo n.º 4
0
    def cols(self):
        """Column headers selected to match the report options."""

        if not hasattr(self, "_cols"):
            if self.include_headers:
                self._cols = []
                if self.include_id:
                    self._cols = ["CDR ID"]
                self._cols.append("Title")
                self._cols.append("Drug Type")
                if self.include_fda_approval:
                    self._cols.append("Accelerated")
                    self._cols.append("Approved in Children")
                if self.include_blank_column:
                    self._cols.append(Reporter.Column("", width="300px"))
            else:
                self._cols = None
        return self._cols
Ejemplo n.º 5
0
class Control(Controller):

    SUBTITLE = "Checked Out Documents"
    COLUMNS = (
        Reporter.Column("Checked Out", width="140px"),
        Reporter.Column("Type", width="150px"),
        Reporter.Column("CDR ID", width="70px"),
        Reporter.Column("Document Title", width="700px"),
    )

    def populate_form(self, page):
        """Show the form fields iff there are any locked documents."""
        if self.lockers:
            fieldset = page.fieldset("Select User")
            page.form.append(fieldset)
            fieldset.append(page.select("User", options=self.lockers))
            page.add_output_options("html")
        else:
            page.form.append(page.B.P("No CDR documents are locked."))
            submit = page.body.xpath("//input[@value='Submit']")
            submit.getparent().remove(submit)

    def run(self):
        """Bypass the Submit button if we already have a user."""
        if not self.request and self.user:
            self.show_report()
        else:
            Controller.run(self)

    @property
    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

    @property
    def user(self):
        """Get the subject of the report.

        This report can be invoked from the web admin menus, where the
        form has the user ID, and from XMetaL, which passes the user
        name instead. We have to be able to handle both.
        """

        if not hasattr(self, "_user"):
            self._user = None
            value = self.fields.getvalue("User")
            if value:
                if value.isdigit():
                    opts = dict(id=int(value))
                else:
                    opts = dict(name=value)
                self._user = self.session.User(self.session, **opts)
        return self._user

    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)
Ejemplo n.º 6
0
class Doctype:
    """Collection of newly published documents of a specific type."""

    COLUMNS = (
        Reporter.Column("CDR ID", width="80px"),
        Reporter.Column("Document Title", width="500px"),
        Reporter.Column("Created By", width="150px"),
        Reporter.Column("Creation Date", width="150px"),
        Reporter.Column("Latest Version Date", width="150px"),
        Reporter.Column("Latest Version By", width="150px"),
        Reporter.Column("Pub?", width="50px"),
        Reporter.Column("Earlier Pub Ver?", width="50px"),
    )

    def __init__(self, control, name):
        """Capture the caller's values.

        Pass:
            control - access to the database
            name - string for this document type's name
        """

        self.__control = control
        self.__name = name

    @property
    def control(self):
        """Access to the database."""
        return self.__control

    @property
    def docs(self):
        """New documents of this type."""

        if not hasattr(self, "_docs"):
            query = self.control.Query("docs_with_pub_status", "*")
            query.order("pv", "cre_date", "ver_date")
            query.where(query.Condition("doc_type", self.name))
            start, end = self.control.start, self.control.end
            if start:
                query.where(query.Condition("cre_date", start, ">="))
            if end:
                end = f"{end} 23:59:59"
                query.where(query.Condition("cre_date", end, "<="))
            rows = query.execute(self.control.cursor).fetchall()
            self._docs = [Doc(self.control, row) for row in rows]
        return self._docs

    @property
    def name(self):
        """String for this document type's name."""
        return self.__name

    @property
    def table(self):
        """Table of new documents (or None if there aren't any)."""

        if not hasattr(self, "_table"):
            self._table = None
            if self.docs:
                opts = dict(caption=self.name, columns=self.COLUMNS)
                rows = [doc.row for doc in self.docs]
                self._table = Reporter.Table(rows, **opts)
        return self._table
Ejemplo n.º 7
0
class Control(Controller):
    """Report logic."""

    SUBTITLE = "Glossary Term Links QC Report"
    NAME_PATH = "/GlossaryTermName/TermName/TermNameString"
    SOURCE_PATH = "/GlossaryTermName/TermName/TermNameSource"
    COLUMNS = (
        Reporter.Column("Doc ID", classes="doc-id"),
        Reporter.Column("Doc Title", classes="doc-title"),
        Reporter.Column("Element Name", classes="element-name"),
        Reporter.Column("Fragment ID", classes="frag-id"),
    )

    def populate_form(self, page):
        """Add the two fields to the form for selecting a term name."""

        fieldset = page.fieldset("Select a Glossary Term by Name or ID")
        fieldset.append(page.text_field("id", label="CDR ID"))
        fieldset.append(page.text_field("name", label="Term Name"))
        page.form.append(fieldset)

    def build_tables(self):
        """Assemble the report tables, one for each linking document type."""

        tables = []
        for doctype in sorted(self.types):
            rows = []
            for linker in self.types[doctype]:
                rows += linker.rows
            opts = dict(cols=self.COLUMNS, caption=doctype, classes="linkers")
            tables.append(self.Reporter.Table(rows, **opts))
        return tables

    def show_report(self):
        """Override the base class version to add a top table and css."""

        css = (
            "#name-and-source tr * { padding: 2px 5px; }",
            "#name-and-source { width: auto; }",
            "#name-and-source th { text-align: right; }",
            ".doc-id { width: 125px; }",
            ".doc-title { width: auto; }",
            ".element-name {width: 150px; }",
            ".frag-id { width: 100px; }",
            "table { width: auto; margin-top: 50px; }",
            "table.linkers { width: 90%; }",
        )
        page = self.report.page
        h2 = page.B.H2("Documents Linked To Glossary Term Names Report")
        h2.set("class", "center")
        page.body.insert(1, h2)
        table = page.B.TABLE(
            page.B.CAPTION("Glossary Term"),
            page.B.TR(page.B.TH("Name"), page.B.TD(self.name)),
            page.B.TR(page.B.TH("Source"), page.B.TD(self.source)))
        table.set("id", "name-and-source")
        page.body.insert(2, table)
        h4 = page.B.H4("Documents Linked to Term Name")
        h4.set("class", "center emphasis")
        page.body.insert(3, h4)
        self.report.page.add_css("\n".join(css))
        self.report.send()

    @property
    def types(self):
        """Dictionary of linking document lists, indexed by document type."""

        if not hasattr(self, "_types"):
            fields = "d.id", "t.name"
            query = self.Query("document d", *fields).unique().order("d.id")
            query.join("doc_type t", "t.id = d.doc_type")
            query.join("query_term l", "l.doc_id = d.id")
            query.where(query.Condition("l.int_val", self.id))
            self._types = {}
            for row in query.execute(self.cursor).fetchall():
                doc = LinkingDoc(self, row.id)
                if row.name not in self._types:
                    self._types[row.name] = [doc]
                else:
                    self._types[row.name].append(doc)
        return self._types

    @property
    def cdr_id(self):
        """String version if the glossary term's CDR ID."""

        if not hasattr(self, "_cdr_id"):
            self._cdr_id = f"CDR{self.id:010d}"
        return self._cdr_id

    @property
    def id(self):
        """Integer for the glossary term name document ID."""

        if not hasattr(self, "_id"):
            id = self.fields.getvalue("id")
            if id:
                try:
                    self._id = Doc.extract_id(id)
                except:
                    self.bail(f"Invalid id: {id!r}")
            else:
                name = self.fields.getvalue("name")
                if not name:
                    self.show_form()
                query = self.Query("query_term", "doc_id").unique()
                query.where(f"path = '{self.NAME_PATH}'")
                query.where(query.Condition("value", name))
                rows = query.execute(self.cursor).fetchall()
                if len(rows) > 1:
                    self.bail(f"Ambiguous term name: {name!r}")
                elif not rows:
                    self.bail(f"Unknown term {name!r}")
                self._id = rows[0].doc_id
        return self._id

    @property
    def name(self):
        """String for the glossary term name."""

        if not hasattr(self, "_name"):
            query = self.Query("query_term", "value")
            query.where(f"path = '{self.NAME_PATH}'")
            query.where(query.Condition("doc_id", self.id))
            rows = query.execute(self.cursor).fetchall()
            self._name = rows[0].value
        return self._name

    @property
    def source(self):
        """Source for the term name, if any."""

        if not hasattr(self, "_source"):
            query = self.Query("query_term", "value")
            query.where(f"path = '{self.SOURCE_PATH}'")
            query.where(query.Condition("doc_id", self.id))
            rows = query.execute(self.cursor).fetchall()
            self._source = rows[0].value if rows else None
        return self._source

    @property
    def cdr_id(self):
        """Display version of the term name document ID."""

        if not hasattr(self, "_cdr_id"):
            self._cdr_id = f"CDR{self.id:010d}"
        return self._cdr_id

    @property
    def xpath(self):
        """Search path for finding links to this document."""

        if not hasattr(self, "_xpath"):
            tests = (
                f"@cdr:ref='{self.cdr_id}'",
                f"@cdr:href='{self.cdr_id}'",
            )
            self._xpath = f"//*[{' or '.join(tests)}]"
        return self._xpath
Ejemplo n.º 8
0
class Control(Controller):
    "Report logic." ""

    SUBTITLE = ("Report on Links From One Section of a Summary "
                "to Another Section")
    COLUMNS = (
        Reporter.Column("FragID", width="75px"),
        Reporter.Column("Target Section/Subsection", width="500px"),
        Reporter.Column("Linking Section/Subsection", width="500px"),
        Reporter.Column("Text in Linking Node", width="500px"),
        Reporter.Column("In Table?", width="75px"),
        Reporter.Column("In List?", width="75px"),
    )

    def populate_form(self, page):
        """Ask the user for a document ID.

        Pass:
            page - HTMLPage object to which the ID field is attached
        """

        fieldset = page.fieldset("Specify a Summary Document")
        fieldset.append(page.text_field("id", label="Summary ID"))
        page.form.append(fieldset)

    def build_tables(self):
        """Return the single table for this report."""
        return self.table

    @property
    def doc(self):
        """The `Doc` object for the report's Summary document."""

        if not hasattr(self, "_doc"):
            self._doc = Doc(self.session, id=self.id)
        return self._doc

    @property
    def format(self):
        """Generate this report as an Excel workbook."""
        return "excel"

    @property
    def id(self):
        """CDR ID of the summary for this report."""
        return self.fields.getvalue("id")

    @property
    def rows(self):
        """Rows for the report table."""

        if not hasattr(self, "_rows"):
            self._rows = []
            for target in sorted(self.targets.values()):
                args = target.id, len(target.links)
                self.logger.debug("target %s has %d links", *args)
                opts = {}
                rowspan = len(target.links)
                if rowspan > 1:
                    opts = dict(rowspan=rowspan)
                link = target.links[0]
                row = [
                    self.Reporter.Cell(target.id, right=True, **opts),
                    self.Reporter.Cell(target.section, **opts),
                    link.section,
                    link.text,
                    self.Reporter.Cell(link.in_table, center=True),
                    self.Reporter.Cell(link.in_list, center=True),
                ]
                self._rows.append(row)
                for link in target.links[1:]:
                    row = [
                        link.section,
                        link.text,
                        self.Reporter.Cell(link.in_table, center=True),
                        self.Reporter.Cell(link.in_list, center=True),
                    ]
                    self._rows.append(row)
            args = len(self._rows), self.doc.cdr_id
            self.logger.info("%d internal links found in %s", *args)
        return self._rows

    @property
    def table(self):
        """Create the table for the document's internal links."""

        if not hasattr(self, "_table"):
            self._table = None
            if self.rows:
                caption = f"Links for CDR{self.doc.id} ({self.doc.title})"
                opts = dict(columns=self.COLUMNS, caption=caption)
                self._table = Reporter.Table(self.rows, **opts)
        return self._table

    @property
    def targets(self):
        """Collect the targets by following the links."""

        if not hasattr(self, "_targets"):
            self._targets = {}
            dead_links = set()
            root = self.doc.root
            opts = dict(namespaces=Doc.NSMAP)
            linking_nodes = root.xpath(self.xpath, **opts)
            for linking_node in linking_nodes:
                link = Link(linking_node)
                if link.id and link.id not in dead_links:
                    if link.id not in self._targets:
                        xpath = f"//*[@cdr:id = '{link.id}']"
                        nodes = root.xpath(xpath, **opts)
                        if len(nodes) > 1:
                            args = len(nodes), self.id
                            self.logger.warning("%d nodes have id %s", *args)
                        if nodes:
                            args = link.id, nodes[0]
                            self._targets[link.id] = Target(*args)
                        else:
                            self.logger.warning("cdr:id %s not found", link.id)
                            dead.add(link.id)
                    if link.id in self._targets:
                        self._targets[link.id].add(link)
        return self._targets

    @property
    def xpath(self):
        """String for finding the linking nodes."""
        return f"//*[starts-with(@cdr:href, '{self.doc.cdr_id}#')]"
Ejemplo n.º 9
0
class DoctypeCounts:
    """Counts by status of new documents of a specific type."""

    COLUMNS = (
        Reporter.Column("Status", width="250px"),
        Reporter.Column("Count", width="75px"),
    )

    def __init__(self, control, name):
        """Save the caller's values.

        Pass:
            control - access to the database
            name - string for this document type's name
        """

        self.__control = control
        self.__name = name

    @property
    def control(self):
        """Access to the database."""
        return self.__control

    @property
    def counts(self):
        """Dictionary of counts per status (None if we have no docs)."""

        if not hasattr(self, "_counts"):
            self._counts = {}
            for doc in self.docs:
                self._counts[doc.status] = self._counts.get(doc.status, 0) + 1
        return self._counts

    @property
    def docs(self):
        """Documents of this type created during the report's date range."""

        if not hasattr(self, "_docs"):
            query = self.control.Query("document d", "d.id")
            query.join("doc_created c", "c.doc_id = d.id")
            query.where(query.Condition("d.doc_type", self.id))
            start, end = self.control.start, self.control.end
            if start:
                query.where(query.Condition("c.created", start, ">="))
            if end:
                end = f"{end} 23:59:59"
                query.where(query.Condition("c.created", end, "<="))
            rows = query.execute(self.control.cursor).fetchall()
            self._docs = [NewDocument(self.control, row.id) for row in rows]
        return self._docs

    @property
    def id(self):
        """Integer unique ID for the document type."""

        if not hasattr(self, "_id"):
            self._id = Doctype(self.control.session, name=self.name).id
        return self._id

    @property
    def name(self):
        """String for this document type's name."""
        return self.__name

    @property
    def rows(self):
        """Sequence of table rows (empty if we have no matching docs)."""

        if not hasattr(self, "_rows"):
            self._rows = []
            if self.counts:
                for name in NewDocument.STATUSES:
                    cell = Reporter.Cell(self.counts.get(name, 0), right=True)
                    self._rows.append((name, cell))
        return self._rows

    @property
    def table(self):
        """Status counts table (or None if we have no matching documents)."""

        if not hasattr(self, "_table"):
            self._table = None
            if self.rows:
                opts = dict(caption=self.name, columns=self.COLUMNS)
                self._table = Reporter.Table(self.rows, **opts)
        return self._table