Skip to content
utils.py 39.2 KiB
Newer Older
def get_facilities_config(request=None):
    # type: (Optional[http.HttpRequest]) -> Optional[Dict[Text, Dict[Text, Any]]]
    from django.conf import settings

    # This is called during offline validation, where Django isn't really set up.
    # The getattr makes this usable.
    facilities = getattr(settings, "RELATE_FACILITIES", None)
    if facilities is None:
        # Only happens during offline validation. Suppresses errors there.
        return None

    if callable(facilities):
        from course.views import get_now_or_fake_time
        now_datetime = get_now_or_fake_time(request)

        result = facilities(now_datetime)
        if not isinstance(result, dict):
            raise RuntimeError("RELATE_FACILITIES must return a dictionary")
        return result
    else:
        return facilities


class FacilityFindingMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        pretend_facilities = request.session.get("relate_pretend_facilities")

        if pretend_facilities is not None:
            facilities = pretend_facilities
        else:
            import ipaddress
            remote_address = ipaddress.ip_address(
                    six.text_type(request.META['REMOTE_ADDR']))
            for name, props in six.iteritems(get_facilities_config(request)):
                ip_ranges = props.get("ip_ranges", [])
                for ir in ip_ranges:
                    if remote_address in ipaddress.ip_network(six.text_type(ir)):
                        facilities.add(name)
        request.relate_facilities = frozenset(facilities)
        return self.get_response(request)


def csv_data_importable(file_contents, column_idx_list, header_count):
    import csv
    spamreader = csv.reader(file_contents)
    n_header_row = 0
    for row in spamreader:
        n_header_row += 1
        if n_header_row <= header_count:
            continue
        try:
            for column_idx in column_idx_list:
                if column_idx is not None:
                    six.text_type(row[column_idx-1])
        except UnicodeDecodeError:
            return False, (
                    _("Error: Columns to be imported contain "
                        "non-ASCII characters. "
                        "Please save your CSV file as utf-8 encoded "
                        "and import again.")
            )
        except Exception as e:
            return False, (
                    string_concat(
                        pgettext_lazy("Starting of Error message",
                            "Error"),
                        ": %(err_type)s: %(err_str)s")
                    % {
                        "err_type": type(e).__name__,
                        "err_str": str(e)}
                    )

def will_use_masked_profile_for_email(recipient_email):
    # type: (Union[Text, List[Text]]) -> bool
    if not recipient_email:
        return False
    if not isinstance(recipient_email, list):
        recipient_email = [recipient_email]
    from course.models import Participation  # noqa
    recepient_participations = (
        Participation.objects.filter(
            user__email__in=recipient_email
        ))
    from course.constants import participation_permission as pperm
    for part in recepient_participations:
        if part.has_permission(pperm.view_participant_masked_profile):
            return True
    return False


# {{{ ipynb utilities


def render_notebook_from_source(
        ipynb_source, clear_output=False, indices=None, clear_markdown=False):
    """
    Get HTML format of ipython notebook so as to be rendered in RELATE flow pages.
    Note: code fences are unconverted, If converted, the result often looked wired,
    e.g., https://stackoverflow.com/a/22285406/3437454, because RELATE will reprocess
    the result again by python-markdown.
    :param ipynb_source: the :class:`text` read from a ipython notebook.
    :param clear_output: a :class:`bool` instance, indicating whether existing
    execution output of code cells should be removed.
    :param indices: a :class:`list` instance, 0-based indices of notebook cells
    which are expected to be rendered.
    :param clear_markdown: a :class:`bool` instance, indicating whether markdown
    cells will be ignored..
    :return:
    """
    import nbformat
    from nbformat.reader import parse_json
    nb_source_dict = parse_json(ipynb_source)

    if indices:
        nb_source_dict.update(
            {"cells": [nb_source_dict["cells"][idx] for idx in indices]})

    if clear_markdown:
        nb_source_dict.update(
            {"cells": [cell for cell in nb_source_dict["cells"]
                       if cell['cell_type'] != "markdown"]})

    nb_source_dict.update({"cells": nb_source_dict["cells"]})

    import json
    ipynb_source = json.dumps(nb_source_dict)
    notebook = nbformat.reads(ipynb_source, as_version=4)

    from traitlets.config import Config
    c = Config()

    # This is to prevent execution of arbitrary code from note book
    c.ExecutePreprocessor.enabled = False
    if clear_output:
        c.ClearOutputPreprocessor.enabled = True

    c.CSSHTMLHeaderPreprocessor.enabled = False
    c.HighlightMagicsPreprocessor.enabled = False

    import os
    from django.conf import settings

    # Place the template in course template dir
    template_path = os.path.join(
        settings.BASE_DIR, "course", "templates", "course", "jinja2")
    c.TemplateExporter.template_path.append(template_path)

    from nbconvert import HTMLExporter
    html_exporter = HTMLExporter(
        config=c,
        template_file="nbconvert_template.tpl"
    )

    (body, resources) = html_exporter.from_notebook_node(notebook)

    return body

# }}}

Andreas Klöckner's avatar
Andreas Klöckner committed
# vim: foldmethod=marker