Ejemplo n.º 1
0
def sync_project_memberships(request, username):
    """Re-sync a user's Keystone project memberships.

    This calls utils.auth.keystone_auth.sync_projects under the hood, which
    will dynamically create missing projects as well.

    Args:
        request (Request): the parent request; used for region detection.
        username (str): the username to sync memberships for.

    Return:
        List[keystone.Project]: a list of Keystone projects the user is a
            member of.
    """
    mapper = ProjectAllocationMapper(request)
    try:
        ks_admin = admin_ks_client(request=request)
        ks_user = get_user(ks_admin, username)

        if not ks_user:
            logger.error((
                "Could not fetch Keystone user for {}, skipping membership syncing"
                .format(username)))
            return

        active_projects = mapper.get_user_projects(username,
                                                   alloc_status=["Active"],
                                                   to_pytas_model=True)

        return sync_projects(ks_admin, ks_user, active_projects)
    except Exception as e:
        logger.error("Could not sync project memberships for %s: %s", username,
                     e)
        return []
Ejemplo n.º 2
0
def add_publications(request, project_id):
    mapper = ProjectAllocationMapper(request)
    try:
        project = mapper.get_project(project_id)
        if project.source != "Chameleon" or not can_add_publications(
            request.user, project
        ):
            raise Http404("The requested project does not exist!")
    except Exception as e:
        logger.error(e)
        raise Http404("The requested project does not exist!")
    if request.POST:
        pubs_form = AddBibtexPublicationForm(request.POST)
        if pubs_form.is_valid():
            bib_database = bibtexparser.loads(pubs_form.cleaned_data["bibtex_string"])
            for entry in bib_database.entries:
                Publication.objects.create_from_bibtex(
                    entry, project, request.user.username
                )
            messages.success(request, "Publication added successfully")
        else:
            messages.error(request, "Error adding publication")
    pubs_form = AddBibtexPublicationForm(initial={"project_id": project.id})

    return render(
        request,
        "projects/add_publications.html",
        {
            "project": project,
            "project_nickname": project.nickname,
            "is_pi": request.user.username == project.pi.username,
            "pubs_form": pubs_form,
            "form": pubs_form,
        },
    )
Ejemplo n.º 3
0
def edit_type(request, project_id):
    form_args = {"request": request}
    if not request.user.is_superuser:
        messages.error(request,
                       "Only the admin users can update project type.")
        return EditTypeForm(**form_args)

    form = EditTypeForm(request.POST, **form_args)
    if form.is_valid(request):
        # try to update type
        try:
            project_type_id = int(form.cleaned_data["typeId"])
            ProjectAllocationMapper.update_project_type(
                project_id, project_type_id)
            form = EditTypeForm(**form_args)
            messages.success(
                request,
                "Update successful! Click <a href='{}'>here</a> to reload".
                format(request.path),
            )
        except Exception:
            logger.exception("Failed to update project type")
            messages.error(request, "Failed to update project type")
    else:
        messages.error(request, "Failed to update project type")

    return form
Ejemplo n.º 4
0
    def can_view(request, artifact):
        all_versions = list(artifact.versions)

        if artifact.sharing_key and (request.GET.get(SHARING_KEY_PARAM)
                                     == artifact.sharing_key):
            return all_versions

        if request.user.is_authenticated():
            if request.user.is_staff:
                return all_versions
            if artifact.created_by == request.user:
                return all_versions
            project_shares = ShareTarget.objects.filter(artifact=artifact,
                                                        project__isnull=False)
            # Avoid the membership lookup if there are no sharing rules in place
            if project_shares:
                mapper = ProjectAllocationMapper(request)
                user_projects = [
                    p['chargeCode']
                    for p in mapper.get_user_projects(request.user.username,
                                                      fetch_balance=False)
                ]
                if any(p.charge_code in user_projects for p in project_shares):
                    return all_versions

        # NOTE(jason): It is important that this check go last. Visibility b/c
        # the artifact has a DOI is the weakest form of visibility; not all
        # versions are necessarily visible in this case. We return a list of
        # the versions that are allowed. We check for stronger visibilty rules
        # first, as they should ensure all versions are seen.
        if artifact.doi:
            return [v for v in all_versions if v.doi]

        return []
Ejemplo n.º 5
0
def dashboard(request):
    context = {}
    # active projects...
    mapper = ProjectAllocationMapper(request)
    active_projects = mapper.get_user_projects(
        request.user.username,
        alloc_status=["Active", "Approved", "Pending"],
        to_pytas_model=True,
    )
    context["active_projects"] = active_projects

    context["show_migration_info"] = request.session.get(
        "has_legacy_account", False)

    # open tickets...
    rt = rtUtil.DjangoRt()
    context["open_tickets"] = rt.getUserTickets(request.user.email)

    # ongoing outages...
    outages = [
        o for o in Outage.objects.order_by("-end_date", "-start_date")
        if not o.resolved
    ]  # silly ORM quirk
    context["outages"] = outages

    webinars = Webinar.objects.filter(end_date__gte=timezone.now())
    context["webinars"] = webinars

    return render(request, "dashboard.html", context)
Ejemplo n.º 6
0
def get_all_alloc(request):
    """Get all allocations, grouped by project.

    Args:
        request: the request that is passed in.

    Raises:
        Exception: when loading projects fails.

    Returns:
        json: dumps all data as serialized json.
    """
    try:
        keycloak_client = KeycloakClient()
        user_attributes = keycloak_client.get_all_users_attributes()
        mapper = ProjectAllocationMapper(request)
        resp = mapper.get_all_projects()
        logger.debug("Total projects: %s", len(resp))
        for r in resp:
            pi_attributes = user_attributes.get(r["pi"]["username"], {})
            if pi_attributes:
                institution = pi_attributes.get("affiliationInstitution", [])
                country = pi_attributes.get("country", [])
                r["pi"]["institution"] = next(iter(institution), None)
                r["pi"]["country"] = next(iter(country), None)
    except Exception as e:
        logger.exception("Error loading chameleon projects")
        messages.error(request, e)
        raise
    return json.dumps(resp)
Ejemplo n.º 7
0
def contact(request):
    resp = {}
    errors = {}
    status = ""
    if request.POST:
        mapper = ProjectAllocationMapper(request)
        data = json.loads(request.body)
        data["rt"]["owner"] = request.user.username

        if data["allocation"]["status"] not in ["Pending", "pending"]:
            errors["allocation"] = "Contacting PI when allocation is pending."

        if len(errors) == 0:
            try:
                mapper.contact_pi_via_rt(data)
                status = "success"
            except Exception:
                logger.exception("Error contacting PI.")
                status = "error"
                errors[
                    "message"] = "An unexpected error occurred. If this problem persists please create a help ticket."

        else:
            logger.info("Request data failed validation. %s",
                        list(errors.values()))
            status = "error"

    else:
        status = "error"
        errors["message"] = "Only POST method allowed."
    resp["status"] = status
    resp["errors"] = errors
    return HttpResponse(json.dumps(resp), content_type="application/json")
Ejemplo n.º 8
0
def profile(request):
    context = {}
    mapper = ProjectAllocationMapper(request)
    context["profile"] = mapper.get_user(request.user)
    context["piEligibility"] = context["profile"]["piEligibility"]

    return render(request, "tas/profile.html", context)
Ejemplo n.º 9
0
def edit_nickname(request, project_id):
    mapper = ProjectAllocationMapper(request)
    project = mapper.get_project(project_id)
    if not project_pi_or_admin_or_superuser(request.user, project):
        messages.error(request, "Only the project PI can update nickname.")
        return EditNicknameForm()

    form = EditNicknameForm(request.POST)
    if form.is_valid(request):
        # try to update nickname
        try:
            nickname = form.cleaned_data["nickname"]
            ProjectAllocationMapper.update_project_nickname(
                project_id, nickname)
            form = EditNicknameForm()
            set_ks_project_nickname(project.chargeCode, nickname)
            messages.success(
                request,
                "Update successful! Click <a href='{}'>here</a> to reload".
                format(request.path),
            )
        except:
            messages.error(request, "Nickname not available")
    else:
        messages.error(request, "Nickname not available")

    return form
Ejemplo n.º 10
0
 def __init__(self, *args, **kwargs):
     if "request" in kwargs:
         request = kwargs["request"]
         del kwargs["request"]
         super(EditTypeForm, self).__init__(*args, **kwargs)
         mapper = ProjectAllocationMapper(request)
         self.fields["typeId"].choices = mapper.get_project_types_choices()
     else:
         logger.error("Couldn't get type list.")
Ejemplo n.º 11
0
 def __init__(self, *args, **kwargs):
     if "request" in kwargs:
         request = kwargs["request"]
         del kwargs["request"]
         super(ProjectCreateForm, self).__init__(*args, **kwargs)
         mapper = ProjectAllocationMapper(request)
         self.fields["fieldId"].choices = mapper.get_fields_choices()
     else:
         logger.error("Couldn't get field list.")
Ejemplo n.º 12
0
def has_active_allocations(request):
    mapper = ProjectAllocationMapper(request)
    user_projects = mapper.get_user_projects(request.user.username,
                                             to_pytas_model=False)
    for project in user_projects:
        for allocation in project["allocations"]:
            if allocation["status"].lower() == "active":
                return True
    return False
Ejemplo n.º 13
0
def custom_login(request, current_app=None, extra_context=None):
    if request.GET.get(settings.FORCE_OLD_LOGIN_EXPERIENCE_PARAM) != '1':
        return HttpResponseRedirect(reverse('oidc_authentication_init'))

    login_return = login(request, current_app=None, extra_context=None)
    password = request.POST.get('password', False)
    if request.user.is_authenticated() and password:
        request.session['is_federated'] = False
        regenerate_tokens(request, password)
        mapper = ProjectAllocationMapper(request)
        mapper.lazy_add_user_to_keycloak()
    return login_return
Ejemplo n.º 14
0
def user_projects(request):
    context = {}

    username = request.user.username
    mapper = ProjectAllocationMapper(request)
    user = mapper.get_user(username)

    context["is_pi_eligible"] = user["piEligibility"].lower() == "eligible"
    context["username"] = username
    context["projects"] = mapper.get_user_projects(username,
                                                   to_pytas_model=True)

    return render(request, "projects/user_projects.html", context)
Ejemplo n.º 15
0
def index_all(request, collection=None):
    user_projects = None
    if request.user.is_authenticated:
        mapper = ProjectAllocationMapper(request)
        user_projects = mapper.get_user_projects(request.user.username,
                                                 fetch_balance=False)
        charge_codes = [p['chargeCode'] for p in user_projects]
        projects = Project.objects.filter(charge_code__in=charge_codes)
        f = (ArtifactFilter.MINE(request) | ArtifactFilter.PROJECT(projects)
             | ArtifactFilter.PUBLIC)
    else:
        f = ArtifactFilter.PUBLIC

    artifacts = _fetch_artifacts(f)
    # Pass user_projects to list to avoid a second fetch of this data
    return _render_list(request, artifacts, user_projects=user_projects)
Ejemplo n.º 16
0
def profile_edit(request):
    mapper = ProjectAllocationMapper(request)
    user_info = mapper.get_user(request.user)

    if request.method == "POST":
        request_pi_eligibility = request.POST.get("request_pi_eligibility")
        department_directory_link = request.POST.get("directorylink")
        kwargs = {"is_pi_eligible": request_pi_eligibility}
        form = UserProfileForm(request.POST, initial=user_info, **kwargs)

        if form.is_valid():
            data = form.cleaned_data
            try:
                mapper.update_user_profile(
                    request.user,
                    data,
                    request_pi_eligibility,
                    department_directory_link,
                )
                messages.success(request, "Your profile has been updated!")
                return HttpResponseRedirect(reverse("tas:profile"))
            except DuplicateUserError:
                messages.error(request, "A user with this email already exists")
            except Exception:
                messages.error(request, "An error occurred updating your profile")
                LOG.exception(
                    (
                        "An unknown error occurred updating user profile for "
                        f"{request.user.username}"
                    )
                )
        else:
            user_info["title"] = form.data.get("title")
    else:
        kwargs = {"is_pi_eligible": False}
        if user_info["piEligibility"].upper() == "ELIGIBLE":
            kwargs = {"is_pi_eligible": True}
        form = UserProfileForm(initial=user_info, **kwargs)

    context = {
        "form": form,
        "user": user_info,
        "piEligibility": user_info["piEligibility"],
        "canRequestPI": can_request_pi(user_info.get("title", "")),
    }
    return render(request, "tas/profile_edit.html", context)
Ejemplo n.º 17
0
 def __init__(self, request, *args, **kwargs):
     super(ShareArtifactForm, self).__init__(*args, **kwargs)
     mapper = ProjectAllocationMapper(request)
     user_projects = [
         (
             project["chargeCode"],
             project["nickname"] if "nickname" in project else project["chargeCode"],
         )
         for project in mapper.get_user_projects(
             request.user.username, to_pytas_model=False
         )
     ]
     self.fields["project"] = forms.ChoiceField(
         label="Belongs to project",
         required=False,
         choices=[(None, "----")] + user_projects,
     )
Ejemplo n.º 18
0
def user_projects(request, username):
    logger.info("User projects requested by admin: %s for user %s",
                request.user, username)
    resp = {"status": "error", "msg": "", "result": []}
    if username:
        try:
            mapper = ProjectAllocationMapper(request)
            user_projects = mapper.get_user_projects(username)
            resp["status"] = "success"
            resp["result"] = user_projects
            logger.info("Total chameleon projects for user %s: %s", username,
                        len(user_projects))
        except Exception as e:
            logger.debug("Error loading projects for user: %s with error %s",
                         username, e)
            resp["msg"] = "Error loading projects for user: %s" % username
    return HttpResponse(json.dumps(resp), content_type="application/json")
Ejemplo n.º 19
0
def _render_list(request, artifacts, user_projects=None):
    if not user_projects:
        if request.user.is_authenticated:
            mapper = ProjectAllocationMapper(request)
            user_projects = mapper.get_user_projects(request.user.username,
                                                     fetch_balance=False)
        else:
            user_projects = []

    template = loader.get_template('sharing_portal/index.html')
    context = {
        'hub_url': settings.ARTIFACT_SHARING_JUPYTERHUB_URL,
        'artifacts': artifacts.order_by('-created_at'),
        'projects': user_projects,
    }

    return HttpResponse(template.render(context, request))
Ejemplo n.º 20
0
def get_all_alloc(request):
    """Get all allocations, grouped by project.

    Args:
        request: the request that is passed in.

    Raises:
        Exception: when loading projects fails.

    Returns:
        json: dumps all data as serialized json.
    """
    try:
        mapper = ProjectAllocationMapper(request)
        resp = mapper.get_all_projects()
        logger.debug("Total projects: %s", len(resp))
    except Exception as e:
        logger.exception("Error loading chameleon projects")
        messages.error(request, e)
        raise
    return json.dumps(resp)
Ejemplo n.º 21
0
def create_supplemental_project(request, artifact):
    mapper = ProjectAllocationMapper(request)

    pi = artifact.project.pi
    artifact_url = request.build_absolute_uri(
        reverse("sharing_portal:detail", kwargs={"pk": artifact.pk}))
    supplemental_project = {
        "nickname": f"reproducing_{artifact.id}",
        "title": f"Reproducing '{artifact.title}'",
        "description":
        f"This project is for reproducing the artifact '{artifact.title}' {artifact_url}",
        "typeId": artifact.project.type.id,
        "fieldId": artifact.project.field.id,
        "piId": artifact.project.pi.id,
    }
    # Approval code is commented out during initial preview release.
    allocation_data = {
        "resourceId": 39,
        "requestorId": pi.id,
        "computeRequested": 1000,
        "status": "approved",
        #"dateReviewed": timezone.now(),
        #"start": timezone.now(),
        #"end": timezone.now() + timedelta(days=6*30),
        #"decisionSummary": "automatically approved for reproducibility",
        #"computeAllocated": 1000,
        #"justification": "Automatic decision",
    }
    supplemental_project["allocations"] = [allocation_data]
    supplemental_project["source"] = "Daypass"
    created_tas_project = mapper.save_project(supplemental_project,
                                              request.get_host())
    # We can assume only 1 here since this project is new
    #allocation = Allocation.objects.get(project_id=created_tas_project["id"])
    #allocation.status = "approved"
    #allocation.save()
    return Project.objects.get(id=created_tas_project["id"])
Ejemplo n.º 22
0
def user_publications(request):
    context = {}
    context["publications"] = []
    pubs = Publication.objects.filter(added_by_username=request.user.username)
    for pub in pubs:
        project = ProjectAllocationMapper.get_publication_project(pub)
        if project:
            context["publications"].append(
                {
                    "title": pub.title,
                    "author": pub.author,
                    "abstract": pub.abstract,
                    "nickname": project.nickname,
                    "chargeCode": project.charge_code,
                }
            )
    return render(request, "projects/view_publications.html", context)
Ejemplo n.º 23
0
def accept_invite(request, invite_code):
    mapper = ProjectAllocationMapper(request)
    invitation = None
    try:
        invitation = Invitation.objects.get(email_code=invite_code)
    except Invitation.DoesNotExist:
        raise Http404("That invitation does not exist!")

    user = request.user
    # Check for existing daypass, use this invite instead
    daypass = get_daypass(user.id, invitation.project.id)
    try:
        if accept_invite_for_user(user, invitation, mapper):
            messages.success(request, "Accepted invitation")
            return HttpResponseRedirect(
                reverse("projects:view_project", args=[invitation.project.id]))
        else:
            messages.error(request, invitation.get_cant_accept_reason())
            return HttpResponseRedirect(reverse("projects:user_projects"))
    finally:
        if daypass is not None:
            daypass.delete()
Ejemplo n.º 24
0
def user_publications(request):
    context = {}
    if "del_pub" in request.POST:
        try:
            del_pub_id = request.POST["pub_ref"]
            logger.debug("deleting publication with id {}".format(del_pub_id))
            Publication.objects.get(pk=del_pub_id).delete()
        except Exception:
            logger.exception("Failed removing publication")
            messages.error(
                request,
                "An unexpected error occurred while attempting "
                "to remove this publication. Please try again",
            )
    context["publications"] = []
    pubs = Publication.objects.filter(added_by_username=request.user.username)
    for pub in pubs:
        project = ProjectAllocationMapper.get_publication_project(pub)
        if project:
            context["publications"].append(
                {
                    "id": pub.id,
                    "title": pub.title,
                    "author": pub.author,
                    "link": "" if not pub.link else pub.link,
                    "forum": pub.forum,
                    "month": ""
                    if not pub.month
                    else datetime.datetime.strptime(str(pub.month), "%m").strftime(
                        "%b"
                    ),
                    "year": pub.year,
                    "nickname": project.nickname,
                    "chargeCode": project.charge_code,
                }
            )
    return render(request, "projects/view_publications.html", context)
Ejemplo n.º 25
0
def create_allocation(request, project_id, allocation_id=-1):
    mapper = ProjectAllocationMapper(request)

    user = mapper.get_user(request.user.username)
    if user["piEligibility"].lower() != "eligible":
        messages.error(
            request,
            "Only PI Eligible users can request allocations. If you would "
            "like to request PI Eligibility, please "
            '<a href="/user/profile/edit/">submit a PI Eligibility '
            "request</a>.",
        )
        return HttpResponseRedirect(reverse("projects:user_projects"))

    project = mapper.get_project(project_id)

    allocation = None
    allocation_id = int(allocation_id)
    if allocation_id > 0:
        for a in project.allocations:
            if a.id == allocation_id:
                allocation = a

    # goofiness that we should clean up later; requires data cleansing
    abstract = project.description
    if "--- Supplemental details ---" in abstract:
        additional = abstract.split("\n\n--- Supplemental details ---\n\n")
        abstract = additional[0]
        additional = additional[1].split("\n\n--- Funding source(s) ---\n\n")
        justification = additional[0]
        if len(additional) > 1:
            funding_source = additional[1]
        else:
            funding_source = ""
    elif allocation:
        justification = allocation.justification
        if "--- Funding source(s) ---" in justification:
            parts = justification.split("\n\n--- Funding source(s) ---\n\n")
            justification = parts[0]
            funding_source = parts[1]
        else:
            funding_source = ""
    else:
        justification = ""
        funding_source = ""

    if request.POST:
        form = AllocationCreateForm(
            request.POST,
            initial={
                "description": abstract,
                "supplemental_details": justification,
                "funding_source": funding_source,
            },
        )
        if form.is_valid():
            allocation = form.cleaned_data.copy()
            allocation["computeRequested"] = 20000

            # Also update the project
            project.description = allocation.pop("description", None)

            supplemental_details = allocation.pop("supplemental_details", None)

            logger.error(supplemental_details)
            funding_source = allocation.pop("funding_source", None)

            # if supplemental_details == None:
            #    raise forms.ValidationError("Justifcation is required")
            # This is required
            if not supplemental_details:
                supplemental_details = "(none)"

            logger.error(supplemental_details)

            if funding_source:
                allocation[
                    "justification"] = "%s\n\n--- Funding source(s) ---\n\n%s" % (
                        supplemental_details,
                        funding_source,
                    )
            else:
                allocation["justification"] = supplemental_details

            allocation["projectId"] = project_id
            allocation["requestorId"] = mapper.get_portal_user_id(
                request.user.username)
            allocation["resourceId"] = "39"

            if allocation_id > 0:
                allocation["id"] = allocation_id

            try:
                logger.info(
                    "Submitting allocation request for project %s: %s" %
                    (project.id, allocation))
                updated_project = mapper.save_project(project.as_dict())
                mapper.save_allocation(allocation, project.chargeCode,
                                       request.get_host())
                messages.success(
                    request, "Your allocation request has been submitted!")
                return HttpResponseRedirect(
                    reverse("projects:view_project",
                            args=[updated_project["id"]]))
            except:
                logger.exception("Error creating allocation")
                form.add_error(
                    "__all__",
                    "An unexpected error occurred. Please try again")
        else:
            form.add_error(
                "__all__",
                "There were errors processing your request. "
                "Please see below for details.",
            )
    else:
        form = AllocationCreateForm(
            initial={
                "description": abstract,
                "supplemental_details": justification,
                "funding_source": funding_source,
            })
    context = {
        "form": form,
        "project": project,
        "alloc_id": allocation_id,
        "alloc": allocation,
    }
    return render(request, "projects/create_allocation.html", context)
Ejemplo n.º 26
0
def view_project(request, project_id):
    mapper = ProjectAllocationMapper(request)
    keycloak_client = KeycloakClient()

    try:
        project = mapper.get_project(project_id)
        if project.source != "Chameleon":
            raise Http404("The requested project does not exist!")
    except Exception as e:
        logger.error(e)
        raise Http404("The requested project does not exist!")

    form = ProjectAddUserForm()
    nickname_form = EditNicknameForm()
    type_form_args = {"request": request}
    type_form = EditTypeForm(**type_form_args)
    pubs_form = AddBibtexPublicationForm()

    can_manage_project_membership, can_manage_project = get_user_permissions(
        keycloak_client, request.user.username, project)

    if (request.POST and can_manage_project_membership
            or is_admin_or_superuser(request.user)):
        form = ProjectAddUserForm()
        if "add_user" in request.POST:
            form = ProjectAddUserForm(request.POST)
            if form.is_valid():
                try:
                    add_username = form.cleaned_data["user_ref"]
                    user = User.objects.get(username=add_username)
                    if mapper.add_user_to_project(project, add_username):
                        messages.success(
                            request,
                            f'User "{add_username}" added to project!')
                        form = ProjectAddUserForm()
                except User.DoesNotExist:
                    # Try sending an invite
                    email_address = form.cleaned_data["user_ref"]
                    try:
                        validate_email(email_address)
                        if email_exists_on_project(project, email_address):
                            messages.error(
                                request,
                                "That email is tied to a user already on the "
                                "project!",
                            )
                        else:
                            add_project_invitation(
                                project_id,
                                email_address,
                                request.user,
                                request,
                                None,
                            )
                            messages.success(request, "Invite sent!")
                    except ValidationError:
                        messages.error(
                            request,
                            ("Unable to add user. Confirm that the username "
                             "is correct and corresponds to a current "
                             "Chameleon user. You can also send an invite "
                             "to an email address if the user does not yet "
                             "have an account."),
                        )
                    except Exception:
                        messages.error(
                            request,
                            "Problem sending invite, please try again.")
                except Exception:
                    logger.exception("Failed adding user")
                    messages.error(request,
                                   "Unable to add user. Please try again.")
            else:
                messages.error(
                    request,
                    ("There were errors processing your request. "
                     "Please see below for details."),
                )
        elif "del_user" in request.POST:
            try:
                del_username = request.POST["user_ref"]
                # Ensure that it's not possible to remove the PI
                if del_username in [project.pi.username, project.pi.email]:
                    raise PermissionDenied(
                        "Removing the PI from the project is not allowed.")
                if mapper.remove_user_from_project(project, del_username):
                    messages.success(
                        request,
                        'User "%s" removed from project' % del_username)
                user = User.objects.get(username=del_username)
                daypass = get_daypass(user.id, project_id)
                if daypass:
                    daypass.delete()
            except PermissionDenied as exc:
                messages.error(request, exc)
            except Exception:
                logger.exception("Failed removing user")
                messages.error(
                    request,
                    "An unexpected error occurred while attempting "
                    "to remove this user. Please try again",
                )
        elif "change_role" in request.POST:
            try:
                role_username = request.POST["user_ref"]
                role_name = request.POST["user_role"].lower()
                keycloak_client.set_user_project_role(role_username,
                                                      get_charge_code(project),
                                                      role_name)
            except Exception:
                logger.exception("Failed to change user role")
                messages.error(
                    request,
                    "An unexpected error occurred while attempting "
                    "to change role for this user. Please try again",
                )
        elif "del_invite" in request.POST:
            try:
                invite_id = request.POST["invite_id"]
                remove_invitation(invite_id)
                messages.success(request, "Invitation removed")
            except Exception:
                logger.exception("Failed to delete invitation")
                messages.error(
                    request,
                    "An unexpected error occurred while attempting "
                    "to remove this invitation. Please try again",
                )
        elif "resend_invite" in request.POST:
            try:
                invite_id = request.POST["invite_id"]
                resend_invitation(invite_id, request.user, request)
                messages.success(request, "Invitation resent")
            except Exception:
                logger.exception("Failed to resend invitation")
                messages.error(
                    request, "An unexpected error occurred while attempting "
                    "to resend this invitation. Please try again")
        elif "nickname" in request.POST:
            nickname_form = edit_nickname(request, project_id)
        elif "typeId" in request.POST:
            type_form = edit_type(request, project_id)

    for a in project.allocations:
        if a.start and isinstance(a.start, str):
            a.start = datetime.strptime(a.start, "%Y-%m-%dT%H:%M:%SZ")
        if a.dateRequested:
            if isinstance(a.dateRequested, str):
                a.dateRequested = datetime.strptime(a.dateRequested,
                                                    "%Y-%m-%dT%H:%M:%SZ")
        if a.dateReviewed:
            if isinstance(a.dateReviewed, str):
                a.dateReviewed = datetime.strptime(a.dateReviewed,
                                                   "%Y-%m-%dT%H:%M:%SZ")
        if a.end:
            if isinstance(a.end, str):
                a.end = datetime.strptime(a.end, "%Y-%m-%dT%H:%M:%SZ")

    users = get_project_members(project)
    if not project_member_or_admin_or_superuser(request.user, project, users):
        raise PermissionDenied

    user_roles = keycloak_client.get_roles_for_all_project_members(
        get_charge_code(project))
    users_mashup = []

    for u in users:
        if u.username == project.pi.username:
            continue
        u_role = user_roles.get(u.username, "member")
        user = {
            "id": u.id,
            "username": u.username,
            "role": u_role.title(),
        }
        try:
            portal_user = User.objects.get(username=u.username)
            user["email"] = portal_user.email
            user["first_name"] = portal_user.first_name
            user["last_name"] = portal_user.last_name
            # Add if the user is on a daypass
            existing_daypass = get_daypass(portal_user.id, project_id)
            if existing_daypass:
                user["daypass"] = format_timedelta(
                    existing_daypass.date_exceeds_duration() - timezone.now())
        except User.DoesNotExist:
            logger.info("user: "******" not found")
        users_mashup.append(user)

    invitations = Invitation.objects.filter(project=project_id)
    invitations = [i for i in invitations if i.can_accept()]

    clean_invitations = []
    for i in invitations:
        new_item = {}
        new_item["email_address"] = i.email_address
        new_item["id"] = i.id
        new_item["status"] = i.status.title()
        if i.duration:
            new_item["duration"] = i.duration
        clean_invitations.append(new_item)

    is_on_daypass = get_daypass(request.user.id, project_id) is not None

    return render(
        request,
        "projects/view_project.html",
        {
            "project": project,
            "project_nickname": project.nickname,
            "project_type": project.type,
            "users": users_mashup,
            "invitations": clean_invitations,
            "can_manage_project_membership": can_manage_project_membership,
            "can_manage_project": can_manage_project,
            "is_admin": request.user.is_superuser,
            "is_on_daypass": is_on_daypass,
            "form": form,
            "nickname_form": nickname_form,
            "type_form": type_form,
            "pubs_form": pubs_form,
            "roles": ROLES,
            "host": request.get_host(),
        },
    )
Ejemplo n.º 27
0
def create_allocation(request, project_id, allocation_id=-1):
    mapper = ProjectAllocationMapper(request)

    user = mapper.get_user(request.user.username)
    if user["piEligibility"].lower() != "eligible":
        messages.error(
            request,
            "Only PI Eligible users can request allocations. If you would "
            "like to request PI Eligibility, please "
            '<a href="/user/profile/edit/">submit a PI Eligibility '
            "request</a>.",
        )
        return HttpResponseRedirect(reverse("projects:user_projects"))

    project = mapper.get_project(project_id)

    allocation = None
    allocation_id = int(allocation_id)
    if allocation_id > 0:
        for a in project.allocations:
            if a.id == allocation_id:
                allocation = a

    abstract = project.description
    if allocation:
        justification = allocation.justification
    else:
        justification = ""

    funding_source = [
        model_to_dict(f)
        for f in Funding.objects.filter(project__id=project_id, is_active=True)
    ]
    # add extra form
    funding_source.append({})

    if request.POST:
        form = AllocationCreateForm(
            request.POST,
            initial={
                "description": abstract,
                "justification": justification,
            },
        )
        formset = FundingFormset(
            request.POST,
            initial=funding_source,
        )
        consent_form = ConsentForm(request.POST)
        if form.is_valid() and formset.is_valid() and consent_form.is_valid():
            allocation = form.cleaned_data.copy()
            allocation["computeRequested"] = 20000

            # Also update the project and fundings
            project.description = allocation.pop("description", None)
            justification = allocation.pop("justification", None)

            allocation["projectId"] = project_id
            allocation["requestorId"] = mapper.get_portal_user_id(
                request.user.username)
            allocation["resourceId"] = "39"
            allocation["justification"] = justification

            if allocation_id > 0:
                allocation["id"] = allocation_id

            try:
                logger.info(
                    "Submitting allocation request for project %s: %s" %
                    (project.id, allocation))
                with transaction.atomic():
                    updated_project = mapper.save_project(project.as_dict())
                    mapper.save_allocation(allocation, project.chargeCode,
                                           request.get_host())
                    new_funding_source = _save_fundings(formset, project_id)
                    _remove_fundings(funding_source, new_funding_source)
                messages.success(
                    request, "Your allocation request has been submitted!")
                return HttpResponseRedirect(
                    reverse("projects:view_project",
                            args=[updated_project["id"]]))
            except:
                logger.exception("Error creating allocation")
                form.add_error(
                    "__all__",
                    "An unexpected error occurred. Please try again")
        else:
            form.add_error(
                "__all__",
                "There were errors processing your request. "
                "Please see below for details.",
            )
    else:
        form = AllocationCreateForm(initial={
            "description": abstract,
            "justification": justification,
        })
        formset = FundingFormset(initial=funding_source)
        consent_form = ConsentForm()
    context = {
        "form": form,
        "funding_formset": formset,
        "consent_form": consent_form,
        "project": project,
        "alloc_id": allocation_id,
        "alloc": allocation,
    }
    return render(request, "projects/create_allocation.html", context)
Ejemplo n.º 28
0
def create_project(request):
    mapper = ProjectAllocationMapper(request)
    form_args = {"request": request}

    user = mapper.get_user(request.user.username)
    if user["piEligibility"].lower() != "eligible":
        messages.error(
            request,
            "Only PI Eligible users can create new projects. "
            "If you would like to request PI Eligibility, please "
            '<a href="/user/profile/edit/">submit a PI Eligibility '
            "request</a>.",
        )
        return HttpResponseRedirect(reverse("projects:user_projects"))
    if request.POST:
        form = ProjectCreateForm(request.POST, **form_args)
        allocation_form = AllocationCreateForm(
            request.POST, initial={"publication_up_to_date": True})
        allocation_form.fields[
            "publication_up_to_date"].widget = forms.HiddenInput()
        funding_formset = FundingFormset(request.POST, initial=[{}])
        consent_form = ConsentForm(request.POST)
        if (form.is_valid() and allocation_form.is_valid()
                and funding_formset.is_valid() and consent_form.is_valid()):
            # title, description, typeId, fieldId
            project = form.cleaned_data.copy()
            allocation_data = allocation_form.cleaned_data.copy()
            # let's check that any provided nickname is unique
            project["nickname"] = project["nickname"].strip()
            nickname_valid = (project["nickname"]
                              and ProjectExtras.objects.filter(
                                  nickname=project["nickname"]).count() < 1
                              and Project.objects.filter(
                                  nickname=project["nickname"]).count() < 1)

            if not nickname_valid:
                form.add_error("__all__", "Project nickname unavailable")
                return render(request, "projects/create_project.html",
                              {"form": form})

            # pi
            pi_user_id = mapper.get_portal_user_id(request.user.username)
            project["piId"] = pi_user_id

            # allocations
            allocation = {
                "resourceId": 39,
                "requestorId": pi_user_id,
                "computeRequested": 20000,
                "justification": allocation_data.pop("justification", None),
            }

            project["allocations"] = [allocation]
            project["description"] = allocation_data.pop("description", None)

            # source
            project["source"] = "Chameleon"
            created_project = None
            try:
                with transaction.atomic():
                    created_project = mapper.save_project(
                        project, request.get_host())
                    _save_fundings(funding_formset, created_project["id"])
                logger.info("newly created project: " +
                            json.dumps(created_project))
                messages.success(request, "Your project has been created!")
                return HttpResponseRedirect(
                    reverse("projects:view_project",
                            args=[created_project["id"]]))
            except:
                # delete project from keycloak
                if created_project:
                    keycloak_client = KeycloakClient()
                    keycloak_client.delete_project(
                        created_project["chargeCode"])
                logger.exception("Error creating project")
                form.add_error(
                    "__all__",
                    "An unexpected error occurred. Please try again")
        else:
            form.add_error(
                "__all__",
                "There were errors processing your request. "
                "Please see below for details.",
            )
    else:
        form = ProjectCreateForm(**form_args)
        allocation_form = AllocationCreateForm(
            initial={"publication_up_to_date": True})
        allocation_form.fields[
            "publication_up_to_date"].widget = forms.HiddenInput()
        funding_formset = FundingFormset(initial=[{}])
        consent_form = ConsentForm()

    return render(
        request,
        "projects/create_project.html",
        {
            "form": form,
            "allocation_form": allocation_form,
            "funding_formset": funding_formset,
            "consent_form": consent_form,
        },
    )
Ejemplo n.º 29
0
def create_project(request):
    mapper = ProjectAllocationMapper(request)
    form_args = {"request": request}

    user = mapper.get_user(request.user.username)
    if user["piEligibility"].lower() != "eligible":
        messages.error(
            request,
            "Only PI Eligible users can create new projects. "
            "If you would like to request PI Eligibility, please "
            '<a href="/user/profile/edit/">submit a PI Eligibility '
            "request</a>.",
        )
        return HttpResponseRedirect(reverse("projects:user_projects"))
    if request.POST:
        form = ProjectCreateForm(request.POST, **form_args)
        if form.is_valid():
            # title, description, typeId, fieldId
            project = form.cleaned_data.copy()
            # let's check that any provided nickname is unique
            project["nickname"] = project["nickname"].strip()
            nickname_valid = (project["nickname"]
                              and ProjectExtras.objects.filter(
                                  nickname=project["nickname"]).count() < 1
                              and Project.objects.filter(
                                  nickname=project["nickname"]).count() < 1)

            if not nickname_valid:
                form.add_error("__all__", "Project nickname unavailable")
                return render(request, "projects/create_project.html",
                              {"form": form})

            project.pop("accept_project_terms", None)

            # pi
            pi_user_id = mapper.get_portal_user_id(request.user.username)
            project["piId"] = pi_user_id

            # allocations
            allocation = {
                "resourceId": 39,
                "requestorId": pi_user_id,
                "computeRequested": 20000,
            }

            supplemental_details = project.pop("supplemental_details", None)
            funding_source = project.pop("funding_source", None)

            # if supplemental_details == None:
            #    raise forms.ValidationError("Justifcation is required")
            if not supplemental_details:
                supplemental_details = "(none)"

            if funding_source:
                allocation[
                    "justification"] = "%s\n\n--- Funding source(s) ---\n\n%s" % (
                        supplemental_details,
                        funding_source,
                    )
            else:
                allocation["justification"] = supplemental_details

            project["allocations"] = [allocation]

            # startup
            project["typeId"] = 2

            # source
            project["source"] = "Chameleon"
            try:
                created_project = mapper.save_project(project,
                                                      request.get_host())
                logger.info("newly created project: " +
                            json.dumps(created_project))
                messages.success(request, "Your project has been created!")
                return HttpResponseRedirect(
                    reverse("projects:view_project",
                            args=[created_project["id"]]))
            except:
                logger.exception("Error creating project")
                form.add_error(
                    "__all__",
                    "An unexpected error occurred. Please try again")
        else:
            form.add_error(
                "__all__",
                "There were errors processing your request. "
                "Please see below for details.",
            )
    else:
        form = ProjectCreateForm(**form_args)

    return render(request, "projects/create_project.html", {"form": form})
Ejemplo n.º 30
0
def view_project(request, project_id):
    mapper = ProjectAllocationMapper(request)
    try:
        project = mapper.get_project(project_id)
        if project.source != "Chameleon":
            raise Http404("The requested project does not exist!")
    except Exception as e:
        logger.error(e)
        raise Http404("The requested project does not exist!")

    form = ProjectAddUserForm()
    nickname_form = EditNicknameForm()
    pubs_form = AddBibtexPublicationForm()

    if request.POST and project_pi_or_admin_or_superuser(
            request.user, project):
        form = ProjectAddUserForm()
        if "add_user" in request.POST:
            form = ProjectAddUserForm(request.POST)
            if form.is_valid():
                try:
                    add_username = form.cleaned_data["username"]
                    if mapper.add_user_to_project(project, add_username):
                        sync_project_memberships(request, add_username)
                        messages.success(
                            request,
                            f'User "{add_username}" added to project!')
                        form = ProjectAddUserForm()
                except Exception as e:
                    logger.exception("Failed adding user")
                    messages.error(
                        request,
                        ("Unable to add user. Confirm that the username is "
                         "correct and corresponds to a current Chameleon user."
                         ),
                    )
            else:
                messages.error(
                    request,
                    ("There were errors processing your request. "
                     "Please see below for details."),
                )
        elif "del_user" in request.POST:
            try:
                del_username = request.POST["username"]
                # Ensure that it's not possible to remove the PI
                if del_username == project.pi.username:
                    raise PermissionDenied(
                        "Removing the PI from the project is not allowed.")
                if mapper.remove_user_from_project(project, del_username):
                    sync_project_memberships(request, del_username)
                    messages.success(
                        request,
                        'User "%s" removed from project' % del_username)
            except PermissionDenied as exc:
                messages.error(request, exc)
            except:
                logger.exception("Failed removing user")
                messages.error(
                    request,
                    "An unexpected error occurred while attempting "
                    "to remove this user. Please try again",
                )
        elif "nickname" in request.POST:
            nickname_form = edit_nickname(request, project_id)

    users = mapper.get_project_members(project)

    if not project_member_or_admin_or_superuser(request.user, project, users):
        raise PermissionDenied

    for a in project.allocations:
        if a.start and isinstance(a.start, str):
            a.start = datetime.strptime(a.start, "%Y-%m-%dT%H:%M:%SZ")
        if a.dateRequested:
            if isinstance(a.dateRequested, str):
                a.dateRequested = datetime.strptime(a.dateRequested,
                                                    "%Y-%m-%dT%H:%M:%SZ")
        if a.dateReviewed:
            if isinstance(a.dateReviewed, str):
                a.dateReviewed = datetime.strptime(a.dateReviewed,
                                                   "%Y-%m-%dT%H:%M:%SZ")
        if a.end:
            if isinstance(a.end, str):
                a.end = datetime.strptime(a.end, "%Y-%m-%dT%H:%M:%SZ")

    user_mashup = []
    for u in users:
        user = {
            "username": u.username,
            "role": u.role,
        }
        try:
            portal_user = User.objects.get(username=u.username)
            user["email"] = portal_user.email
            user["first_name"] = portal_user.first_name
            user["last_name"] = portal_user.last_name
        except User.DoesNotExist:
            logger.info("user: "******" not found")
        user_mashup.append(user)

    return render(
        request,
        "projects/view_project.html",
        {
            "project": project,
            "project_nickname": project.nickname,
            "users": user_mashup,
            "is_pi": request.user.username == project.pi.username,
            "form": form,
            "nickname_form": nickname_form,
            "pubs_form": pubs_form,
        },
    )