def __init__(self, filename: str, debug=False):

        self.__html_style = """"""

        # Open head and body streams (for reading and writing)
        self.__html_head = Buffer()
        self.__html_body = Buffer()

        self.__debug = debug

        # Name of the output file
        self.__webpage_filename = filename
Beispiel #2
0
    def __init__(self, tabs=None, plot_webpage_name: str = "index.html"):
        if tabs is None:
            tabs = {}

        self.tabs: Dict[str, Dict[str, Union[str, List]]] = {
            key: {
                "data": [],
                "directory": value
            }
            for (key, value) in tabs.items()
        }
        self.data = Buffer()
        self.name: str = plot_webpage_name

        self.uploads_days_ago: List[int] = []
        self.uploads_z: List[float] = []
        self.run_links: List[str] = []

        self.N_individual_runs: int = 0
        self.N_comparisons: int = 0
        self.N_ongoing_runs: int = 0
Beispiel #3
0
class Tree:

    # To find out how long ago a page was created
    current_time = datetime.now()

    # Html is wield
    white_space: str = "&nbsp "

    # Display used parameters in html format
    run_param_page_icon = f"""<i style="float:right; color:#A9A9A9; font-size:13px" class="fa"> &#xf013 {white_space}</i>"""
    used_param_file_name = "used_parameters.html"

    # Link and icon for morphology page
    morphology_page_icon = f"""<i style="float:right; color:#FF8C00; font-size:18px" class="fa">&#xf06e {white_space}</i>"""
    morphology_file_name = "morphology.html"

    # Categories to look for
    individual = "individual_runs"
    comparison = "comparisons"

    def __init__(self, tabs=None, plot_webpage_name: str = "index.html"):
        if tabs is None:
            tabs = {}

        self.tabs: Dict[str, Dict[str, Union[str, List]]] = {
            key: {
                "data": [],
                "directory": value
            }
            for (key, value) in tabs.items()
        }
        self.data = Buffer()
        self.name: str = plot_webpage_name

        self.uploads_days_ago: List[int] = []
        self.uploads_z: List[float] = []
        self.run_links: List[str] = []

        self.N_individual_runs: int = 0
        self.N_comparisons: int = 0
        self.N_ongoing_runs: int = 0

    def find_creating_time(self, path_to_dir: str) -> int:
        """
        Compute the time difference between one and when the __plot_webpage_name
        file was created

        Parameters
        -------
        path_to_dir: str

        Returns
        -------
        output: int
        Number of days ago
        """

        time = os.stat(f"{path_to_dir}/{self.name}").st_mtime
        dt = datetime.fromtimestamp(time)
        days_ago = (self.current_time - dt).days

        return days_ago

    def days_ago_to_string(self, days_ago: int) -> str:
        """
        Formats the number of days ago

        Parameters
        -------
        days_ago: int

        Returns
        -------
        output: str
        Number of days ago formatted
        """

        if days_ago > 1:
            string = Content(f"{days_ago:d} days ago")
        elif days_ago == 1:
            string = Content(f"{days_ago:d} day ago")
        else:
            string = Content(f"{days_ago:d} New")

        string.wrap_text(block="i")
        string.wrap_text(block="span", span_style="float:right; color:#0000FF")

        return string.getter()

    def walk(
        self,
        abs_path: str,
        depth: int = 0,
        visual_line: str = "",
        prefix: str = "/cosma/home/www/swift.dur.ac.uk/public_html",
    ):
        """
        This function traverses the directory tree collecting necessary information in
        order to create a general html page with the runs' info and statistics
        """

        relative_path = abs_path.replace(prefix, "")

        # Number of sub-directions in the current directory
        list_dir = sorted(os.listdir(abs_path))
        N_of_dir = sum(
            [1 for d in list_dir if os.path.isdir(os.path.join(abs_path, d))])

        # Loop over all sub-directories and files in the current directory
        for dir_counter, dir_name in enumerate(list_dir):

            # Absolute path to the file/sub-directory
            absolute_dir_path = os.path.join(abs_path, dir_name)

            # Do not do anything if it is a file
            if os.path.isdir(absolute_dir_path):

                if absolute_dir_path.find(self.individual) > -1:
                    self.N_individual_runs += 1
                elif absolute_dir_path.find(self.comparison) > -1:
                    self.N_comparisons += 1
                else:
                    continue

                # Number of sub-directories in a given sub-directory of this directory.
                # Zero means leaf node
                N_of_subdir = sum([
                    1 for sub_dir in os.scandir(absolute_dir_path)
                    if os.path.isdir(os.path.join(abs_path, sub_dir))
                ])

                is_leaf = N_of_subdir == 0

                # Check if the directory is a leaf node
                if is_leaf:

                    # Time data
                    days_ago = self.find_creating_time(absolute_dir_path)
                    modified = self.days_ago_to_string(days_ago=days_ago)
                    self.uploads_days_ago.append(days_ago)

                    link_to_plots = f"{relative_path}/{dir_name}/{self.name}"
                    morphology = ""
                    settings = ""

                    if os.path.isfile(
                            f"{absolute_dir_path:}/{self.used_param_file_name}"
                    ):
                        settings = f"""<a href="{relative_path}/{dir_name}/{self.used_param_file_name}" target="_blank">
                          {self.run_param_page_icon}</a>"""

                    if os.path.isfile(
                            f"{absolute_dir_path:}/{self.morphology_file_name}"
                    ):
                        settings = f"""<a href="{relative_path}/{dir_name}/{self.morphology_file_name}" target="_blank">
                          {self.morphology_page_icon}</a>"""

                    text = f"{dir_name}{modified}"
                    link_list = f"<li><a href={link_to_plots}>{text}</a>{settings}{morphology}</li>"
                    self.run_links.append(link_list)

                    text = f"{visual_line} |_{dir_name}{modified}"
                    link_tree = f"<li><a href={link_to_plots}>{text}</a>{settings}{morphology}</li>"
                    self.data.write_to_buffer(link_tree)

                    try:
                        redshift = float(dir_name[1:4])
                        self.uploads_z.append(redshift)
                    except ValueError:
                        pass

                    # Extra tabs
                    for (tab_name, tab_content) in self.tabs.items():
                        if link_to_plots.find(tab_content["directory"]) > -1:
                            string_split = text.split()
                            if (string_split[:3] ==
                                    f"{self.white_space} {self.white_space} |".
                                    split()):
                                arg = text.find("|")
                                text = f" {self.white_space * 3}{text[arg + 2:]}"

                            elif string_split[:
                                              3] == f"| {self.white_space} |".split(
                                              ):
                                indent = 8
                                arg = text[indent:].find("|")
                                text = f" | {self.white_space * 2}{text[indent + arg + 1:]}"

                            string = f"<li><a href={link_to_plots}>{text}</a>{settings:s}{morphology:s}</li>"
                            self.tabs[tab_name]["data"].append(string)

                # Not a leaf
                else:
                    text = f"{visual_line} |_<b> {dir_name} </b>"
                    string = f"<li>{text}</li>"
                    self.data.write_to_buffer(string)
                    rel_path_dir = f"{relative_path}/{dir_name}"

                    # Extra tabs
                    if dir_name == self.individual or dir_name == self.comparison:
                        for tab_name in self.tabs.keys():
                            self.tabs[tab_name]["data"].append(string)

                    for (tab_name, tab_content) in self.tabs.items():
                        if rel_path_dir.find(tab_content["directory"]) > -1:
                            string_split = text.split()

                            if (string_split[:3] ==
                                    f"{self.white_space} {self.white_space} |".
                                    split()):
                                arg = text.find("|")
                                text = f" {self.white_space * 3}{text[arg + 2:]}"

                            elif string_split[:
                                              3] == f"| {self.white_space} |".split(
                                              ):
                                indent = 8
                                arg = text[indent:].find("|")
                                text = f" | {self.white_space * 2}{text[indent + arg + 1:]}"

                            self.tabs[tab_name]["data"].append(
                                f"<li>{text}</li>")

                # Evolve tree representation
                if dir_counter < N_of_dir - 1:
                    new_visual_line = f"{visual_line} | {self.white_space} "
                else:
                    new_visual_line = f"{visual_line} {self.white_space * 2}"

                # Recurse
                self.walk(
                    absolute_dir_path,
                    depth + 1,
                    new_visual_line,
                )
class HtmlPage:

    white_space: str = "&nbsp "

    def __init__(self, filename: str, debug=False):

        self.__html_style = """"""

        # Open head and body streams (for reading and writing)
        self.__html_head = Buffer()
        self.__html_body = Buffer()

        self.__debug = debug

        # Name of the output file
        self.__webpage_filename = filename

    def __del__(self):

        if self.__debug:
            print("Destruction: Closing head and body streams")

        # Close the streams
        del self.__html_head, self.__html_body

        return

    def write_head(self, text: str):
        """
        Writes to head of webpage


        Parameters
        -------

        text: str
        text to write to the head
        """

        self.__html_head.write_to_buffer(text=text)
        return

    def write_body(self, text: str):
        """
        Writes to the body of webpage


        Parameters
        -------

        text: str
        text to write to the body
        """
        self.__html_body.write_to_buffer(text=text)
        return

    def render_webpage(self) -> str:
        """
        Renders and returns the html page

        Returns
        -------

        output: str
        """

        webpage = f"""
                  <!DOCTYPE html>
                  <html>
                    <head>
                      {self.__html_style}
                      {self.__html_head.get_content()}
                    </head>
                    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
                    <body>
                      {self.__html_body.get_content()}
                    </body>
                  </html>
                  """
        return webpage

    def set_style(self, style: str):
        """
        Sets up webpage style

        Parameters
        -------

        style: str
        style in the html format
        """

        self.__html_style = style
        return

    def load_style(self, path_to_file: str):
        """
        Loads page style from the provided file and applies it to the webpage

        Parameters
        -------

        path_to_file: str
        path to the file with the style for webpage
        """

        if path.exists(path_to_file):
            with open(path_to_file) as f:

                # Read style
                loaded_style = f.read()

                # Apply style
                self.set_style(style=loaded_style)
        else:
            raise IOError(f"File '{path_to_file}' does not exist!")

        return

    def save_webpage(self):
        """
        Builds and saves the webpage into a file. If the file
        already exists, overwrites it.
        """

        with open(self.__webpage_filename, "w+") as out:

            if self.__debug:
                print(f"Saving webpage into {self.__webpage_filename}...")

            webpage = self.render_webpage()
            out.write(webpage)

        return

    def visualise_webpage(self):
        """
        Opens the webpage using the default browser
        """

        # Create and save webpage if the file does not exist yet
        self.save_webpage()

        webbrowser.open_new_tab(self.__webpage_filename)

        return

    def create_tab_panel_open(self, tabs: Optional[Dict[str, str]] = None):
        """
        Creates tabs with different content. Start the tab panel block

        Parameters
        -------
        tabs: Optional[Dict[str, str]]
        Dictionary containing the tabs
        """

        if tabs is None:
            tabs = {}

        self.__html_body.write_to_buffer("""<div class="tab">""")

        for (name, display_name) in tabs.items():

            button_attrs = f"""tablinks" onclick="openTab(event, '{name}')"""
            button = Content(display_name)
            button.wrap_text(block="button", button_class=button_attrs)

            self.__html_body.write_to_buffer(button.getter())

        self.__html_body.write_to_buffer("""</div>""")

        return

    def create_tab_panel_close(self, tab_content: str = "tabcontent"):
        """
        Finish the tab panel block

        Parameters
        -------

        tab_content: str
        Tab content object
        """
        self.__html_body.write_to_buffer(f"""<script>
                  function openTab(evt, TabName) 
                  {{
                    var i, {tab_content}, tablinks;
                    tabcontent = document.getElementsByClassName("{tab_content}");
                    for (i = 0; i < {tab_content}.length; i++) {{
                      {tab_content}[i].style.display = "none";
                    }}
                  
                    tablinks = document.getElementsByClassName("tablinks");
                  
                    for (i = 0; i < tablinks.length; i++) {{
                      tablinks[i].className = tablinks[i].className.replace(" active", "");
                    }}
                  
                    document.getElementById(TabName).style.display = "block";
                    evt.currentTarget.className += " active";
                  }}
                </script>
            """)

        return
def create_table_with_links_to_reports(file_name: str = "reports.json") -> str:
    """
    Reads off reports' titles and the links to the reports.
    Creates a table with this info on the main html webpage.

    Parameters
    -------
    file_name: str
    File with the report names and links

    Returns
    -------
    output: str
    """

    try:
        with open(file_name, "r") as f:
            data = json.load(f)

    except FileNotFoundError as err:
        error_message = (
            f"The file with the reports' links '{file_name:s}' is not found.")
        raise RuntimeError(error_message) from err

    buffer = Buffer()
    buffer.write_to_buffer("<h2>Reports</h2>")

    for author, reports in data.items():
        if reports:
            buffer.write_to_buffer(f"<i>Author: {author:s}</i><ol>")
            for name, link in reports.items():
                buffer.write_to_buffer(
                    f"<li><a href={link:s}>{name:s}</a></li>")
            buffer.write_to_buffer("</ol>")

    return buffer.get_content()
def create_webpage(
    filename: str = "index.html",
    path_to_style: str = "./html/style.html",
    debug: bool = False,
):
    """
    Create the webpage step by step

    Parameters
    -------
    filename: str
    filename for the webpage

    path_to_style: str
    Path to file with a style for webpage

    debug: bool
    Extra output and checks for debugging?
    """

    today_pretty_format = datetime.now().strftime("%d/%m/%Y at %H:%M:%S")

    obj = HtmlPage(filename=filename, debug=debug)
    obj.load_style(path_to_file=path_to_style)

    tabs = {
        "full_tree": "Full tree",
        "date": "Sort by date",
        "redshift": "Sort by redshift",
    }
    extra_tabs = {"xmas2020": "NEWEST", "hawk": "HAWK", "zooms": "ZOOMS"}
    tabs.update(extra_tabs)

    # Traverse tree
    PATH = "/home/evgenii/Desktop/PhD/shell/webpage/explore"
    tree = traverse_tree(initial_path=PATH, tabs=tabs)
    tree_content = Content(tree.data.get_content())

    # Compute additional properties
    reports = create_table_with_links_to_reports()
    current_runs, redshifts_ongoing = fetch_current_runs_names()
    plots = create_plots(tree, ongoing_runs=redshifts_ongoing)

    # Flex container
    obj.write_body(
        """<div class="container" style="display: flex; max-width: 2500px;">"""
    )
    obj.write_body("""<div class="content">""")

    # Open tabs
    obj.create_tab_panel_open(tabs=tabs)

    tab_tree_top = f"""<h1>Path to current directory: {PATH} 
                      <span style="float:right; color:blue;"> Uploaded
                      </span>
                      </h1>"""

    # Tab 1
    tab1 = Content(tab_tree_top)
    tree_content.wrap_text(block="ul")
    tab1.append_text(tree_content.getter())
    tab1.wrap_text(
        block="div",
        div_id="full_tree",
        div_class="tabcontent",
        div_style="display:none",
    )
    obj.write_body(tab1.getter())

    # Tab 2
    tab2 = Content(tab_tree_top)
    buffer2 = Buffer()
    for item_arg in np.argsort(tree.uploads_days_ago):
        buffer2.write_to_buffer(tree.run_links[item_arg])
    buffer2_out = Content(buffer2.get_content())
    buffer2_out.wrap_text("ol")
    tab2.append_text(buffer2_out.getter())
    tab2.wrap_text(block="div",
                   div_id="date",
                   div_class="tabcontent",
                   div_style="display:none")
    obj.write_body(tab2.getter())

    # Tab 3
    tab3 = Content(tab_tree_top)
    buffer3 = Buffer()
    for item_arg in np.argsort(tree.uploads_z):
        buffer3.write_to_buffer(tree.run_links[item_arg])
    buffer3_out = Content(buffer3.get_content())
    buffer3_out.wrap_text("ol")
    tab3.append_text(buffer3_out.getter())
    tab3.wrap_text(block="div",
                   div_id="redshift",
                   div_class="tabcontent",
                   div_style="display:none")
    obj.write_body(tab3.getter())

    # Extra tabs
    for tab_name in extra_tabs:
        tabn = Content(tab_tree_top)
        buffern = Buffer()
        for string in tree.tabs[tab_name]["data"]:
            buffern.write_to_buffer(string)
        buffern_out = Content(buffern.get_content())
        buffern_out.wrap_text("ul")
        tabn.append_text(buffern_out.getter())
        tabn.wrap_text(
            block="div",
            div_id=tab_name,
            div_class="tabcontent",
            div_style="display:none",
        )
        obj.write_body(tabn.getter())

    # Close left column
    obj.write_body("</div>")
    # Add script to make tab panel interactive
    obj.create_tab_panel_close(tab_content="tabcontent")

    # Open middle column
    obj.write_body(
        """<div class="content"; style="margin-left: 1em; max-width: 550px;">"""
    )

    # Block with general information
    tab = Content(f"""
        <h2>General information</h2><ul>
        <li>The page was last updated on 
        <b>{today_pretty_format:s}</b></li>
        <li>Number of ongoing runs: <b>{len(redshifts_ongoing):d}</b></li>
        <li>Number of analysed runs [individual]: 
        <b>{tree.N_individual_runs:d}</b></li>
        <li>Number of analysed runs [comparisons]: 
        <b>{tree.N_comparisons:d}</b></li><p>{tree.white_space}</p>
        </ul>""")
    tab.wrap_text(block="div", div_class="content1")
    obj.write_body(tab.getter())

    obj.write_body(reports)
    obj.write_body(current_runs)

    # Close middle column
    obj.write_body("""</div>""")

    # Plots to the third column
    obj.write_body(
        """<div class="content"; style="margin-left: 1em; max-width: 330px">"""
    )
    obj.write_body(plots)
    obj.write_body("</div>")

    obj.visualise_webpage()
def fetch_current_runs_names(
        file_with_redshifts: str = "./output_list.txt",
        file_pattern: str = "colibre*") -> Tuple[str, List[str]]:
    """
    Find currently ongoing runs

    Parameters
    -------
    file_with_redshifts: str
    File containing the redshifts

    file_pattern: str
    File patter to use when looking for matches

    Returns
    -------
    output: str
    """

    run_names = []
    run_redshifts = []

    try:
        output_z = open(file_with_redshifts, "r")
        run_z_list = []
        for line in output_z.readlines():
            try:
                redshift = "{:.2f}".format(float(line.split(", ")[0]))
                run_z_list.append(redshift)
            # Skip header if there is one
            except ValueError:
                pass

        # See the ongoing runs
        process = subprocess.run(["squeue", "-u", "dc-chai1", "-o", "%Z"],
                                 stdout=subprocess.PIPE)
        paths = process.stdout.decode("utf-8").split("\n")

        # Loop over paths to ongoing runs
        for path in paths:

            # Looking for names
            file_paths = glob.glob(f"{path}/{file_pattern}.yml")
            if file_paths:
                with open(file_paths[0], "r") as f:
                    file_lines = f.readlines()
                    for l in file_lines:
                        if l.__contains__("run_name:"):
                            run_names.append(l.split()[-1])
            else:
                run_names.append("None")

            # Looking for redshifts
            file_paths = sorted(glob.glob(f"{path}/{file_pattern}.hdf5"))

            if file_paths:
                # The path must be of the form "*/colibre_????.hdf5"
                snp_nums = [
                    int(path.split("/")[-1][8:-5]) for path in file_paths
                ]

                # Snapshot with the highest number corresponds to the lowest redshift
                N_latest_snp = sorted(snp_nums)[-1]

                try:
                    z = run_z_list[N_latest_snp]
                except IndexError:
                    z = "0.5"

                run_redshifts.append(z)
            else:
                run_redshifts.append("Enqueued")

        #    return run_names, run_redshifts
        run_names = run_names[1:-1]
        run_redshifts = run_redshifts[1:-1]

    except (IOError, ValueError):
        print(f"File {file_with_redshifts:s} cannot be open")

    # Block with the info on ongoing runs
    buffer = Buffer()
    buffer.write_to_buffer("""
        <h2>Names of ongoing runs<span style="float:right; color:black">Current z</span></h2>
        <ol>""")

    for idx in np.argsort(run_redshifts):
        if run_names[idx] != "None":
            buffer.write_to_buffer(f"""<li>{run_names[idx]:s} 
            <span style="float:right;">{ run_redshifts[idx]:s}</span></li>""")
    buffer.write_to_buffer("</ol>")

    return buffer.get_content(), run_redshifts