def test_project_submission_to_approved_community(self):
        """
        This tests submitting a project to a community that is approved to participate in this round.
         - Create a new RoundPage for the upcoming round, with an approved community
         - Go to the community read-only page
         - Log in as a mentor from the previous round for this community
         - Click the 'Submit Project' button, fill out project description
         - Be redirected to the MentorApproval form, fill that out
         - Be redirected to the ProjectSkills form, fill that out
         - Be redirected to the ProjectCommunicationChannels form, fill that out
         - Finally be done! \o/
         - Coordinator receives email about the pending project
         - The community read-only page should not reflect the project was submitted, except to the mentor who submitted it
        """
        scenario = scenarios.InternshipWeekScenario(week=10,
                                                    community__name='Debian',
                                                    community__slug='debian')

        current_round = factories.RoundPageFactory(start_from='pingnew')
        participation = factories.ParticipationFactory(
            community=scenario.community,
            participating_round=current_round,
            approval_status=ApprovalStatus.APPROVED)

        self.check_project_submission(scenario, current_round, participation)
    def test_sponsor_info_visible(self):
        """
        This tests that sponsor information is visible to staff.
         - Create a community with one sponsor, who is approved to participate in the current round
         - Create a second community with two sponsors, who is approved to participate
         - Check that the sponsor page is visible
        """
        current_round = factories.RoundPageFactory(start_from='pingnew')

        # Create a community with one sponsor, who is approved to participate in the current round
        sponsorship_a = factories.SponsorshipFactory(
            participation__participating_round=current_round,
            participation__approval_status=models.ApprovalStatus.APPROVED,
            name="Sponsor A",
            amount=13000,
            funding_secured=True,
        )

        # Create a second community with one sponsor, who is approved to participate
        sponsorship_b = factories.SponsorshipFactory(
            participation__participating_round=current_round,
            participation__approval_status=models.ApprovalStatus.APPROVED,
            name="Sponsor B",
            amount=6500,
            funding_secured=True,
        )

        # Add a second sponsor to the second community
        sponsorship_c = factories.SponsorshipFactory(
            participation=sponsorship_b.participation,
            name="Sponsor C",
            amount=19500,
            funding_secured=True,
        )

        organizer = factories.ComradeFactory()
        organizer.account.is_staff = True
        organizer.account.save()

        self.client.logout()
        self.client.force_login(organizer.account)

        response = self.client.get(
            reverse('sponsor-info', kwargs={'round_slug': current_round.slug}))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<td>Sponsor A</td>', html=True)
        self.assertContains(response, '<td>Sponsor B</td>', html=True)
        self.assertContains(response, '<td>Sponsor C</td>', html=True)
        self.assertContains(response,
                            '<h3>{}</h3>'.format(
                                sponsorship_a.participation.community.name),
                            html=True)
        self.assertContains(response,
                            '<h3>{}</h3>'.format(
                                sponsorship_b.participation.community.name),
                            html=True)
Beispiel #3
0
    def test_initial_application_form_closed_after_period(self):
        """
        This tests that the initial application form is closed after the initial application deadline
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_close')
        applicant = factories.ComradeFactory()
        self.client.force_login(applicant.account)

        response = self.client.get(reverse('eligibility'))
        self.assertEqual(response.status_code, 403)
Beispiel #4
0
    def test_applicants_cannot_see_sensitive_data(self):
        """
        This tests that applicants cannot see their essay answers after the initial application was submitted.
        The round is in the initial application period.
        /eligibility-results/ should NOT show the essay answers.
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')
        applicant_approval = self.create_initial_application(current_round)

        self.check_essay_hidden_from_eligibility_results(applicant_approval)
Beispiel #5
0
    def test_initial_application_form_open_during_period(self):
        """
        This tests that the initial application form is open during the initial application period
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')
        applicant = factories.ComradeFactory()
        self.client.force_login(applicant.account)

        response = self.client.get(reverse('eligibility'))
        self.assertEqual(response.status_code, 200)
    def test_new_coordinator_signs_up_community_to_participate(self):
        """
        This tests submitting a new community to participate in this round.
         - Create a new RoundPage for the upcoming round where the CFP is open
         - Check that the community CFP page shows community participation sign up is open
        """
        current_round = factories.RoundPageFactory(start_from='pingnew')

        self.client.logout()
        self.check_community_signup_marked_open()

        new_community_signup_path = reverse('community-add')
        response = self.client.get(new_community_signup_path)
        self.assertRedirects(
            response,
            settings.LOGIN_URL + '?next={}'.format(new_community_signup_path))

        # Assume Comrade account sign up works - TODO: test this separately
        coordinator = factories.ComradeFactory()
        self.client.force_login(coordinator.account)
        response = self.client.post(
            new_community_signup_path,
            {
                'name': 'Debian',
                'reason_for_participation':
                'We want more diversity in our community.',
                'mentorship_programs':
                'We have participated in Google Summer of Code since 2013.',
                'approved_license': 'on',
                'no_proprietary_software': 'on',
                'participating_orgs':
                'Debian is comprised of volunteers from around the world. Some corporations pay maintainers to participate.',
                'approved_advertising': 'on',
            },
            follow=True,
        )

        # Ensure the Community object and NewCommunity object was created
        community = Community.objects.get(name='Debian')
        newcommunity = NewCommunity.objects.get(community=community)
        coordinatorapproval = CoordinatorApproval.objects.get(
            coordinator=coordinator,
            community=community,
            approval_status=ApprovalStatus.APPROVED)

        new_community_participation_path = reverse('participation-action',
                                                   kwargs={
                                                       'action':
                                                       'submit',
                                                       'round_slug':
                                                       current_round.slug,
                                                       'community_slug':
                                                       community.slug,
                                                   })
        self.assertRedirects(response, new_community_participation_path)
    def setup_internship_round_and_reviewer(self):
        current_round = factories.RoundPageFactory(
            start_from='initial_applications_open')

        reviewer = self.create_applicant_reviewer(
            current_round, models.ApprovalStatus.APPROVED)
        # Only accounts with the Django staff privilege can approve initial applications
        # Organizers can both be staff and an initial application reviewer
        reviewer.account.is_staff = True
        reviewer.account.save()
        self.client.force_login(reviewer.account)
        return (current_round, reviewer)
Beispiel #8
0
    def test_initial_application_marked_closed(self):
        """
        This tests how the website works before we start accepting initial applications
         - Create a new RoundPage for the upcoming round where initial_applications_open has not passed
         - /apply/eligibility/ should not have a button to submit an application
         - it should not have a prompt to submit an application
         - it should have a prompt saying initial applications are currently closed
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open', days_after_today=1)

        response = self.client.get(reverse('eligibility-information'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<p>The application system for Outreachy internships is currently closed. The application system will be available when the application period opens on {} at 4pm UTC. Initial applications are due on {} 4pm UTC.</p>'.format(date_format(current_round.initial_applications_open), date_format(current_round.initial_applications_close)), html=True)
Beispiel #9
0
    def test_community_participation_signup_too_late(self):
        """
        This tests submitting an older community to participate in this round.
         - Create a community that has been approved to participate in a past round
           (the past round is currently in week 10 of the internship)
         - Create a new RoundPage for the upcoming round where the CFP is closed to new communities
         - Try to submit the community to participate in the round through the form
         - It should fail
        """
        current_round = factories.RoundPageFactory(start_from='lateorgs')

        self.check_community_signup_marked_closed()
        self.submit_failed_community_signup(current_round)
Beispiel #10
0
    def test_sensitive_data_removed_on_processing(self):
        """
        This tests that approved applicant reviewers can see applicant essay answers after the initial application was submitted.
        The round is in the initial application period.
        /applicant-review-detail/ should show the essay answers.
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')

        reviewer = self.create_applicant_reviewer(current_round, models.ApprovalStatus.APPROVED)
        essay_quality = self.create_essay_quality()

        # Only accounts with the Django staff privilege can approve initial applications
        # Organizers can both be staff and an initial application reviewer
        reviewer.account.is_staff = True
        reviewer.account.save()
        self.client.force_login(reviewer.account)

        for approval_status in [ models.ApprovalStatus.APPROVED, models.ApprovalStatus.REJECTED ]:
            with self.subTest(approval_status=approval_status):

                applicant_approval = self.create_initial_application(current_round)
                self.create_essay_review(reviewer, applicant_approval, essay_quality)

                if approval_status == models.ApprovalStatus.APPROVED:
                    response = self.client.post(applicant_approval.get_approve_url())
                elif approval_status == models.ApprovalStatus.REJECTED:
                    response = self.client.post(applicant_approval.get_reject_url())

                self.assertRedirects(response, applicant_approval.get_preview_url())

                # Reload the object from the database after the invoked view modifies the database
                applicant_approval = models.ApplicantApproval.objects.get(pk=applicant_approval.pk)
                self.assertEqual(applicant_approval.approval_status, approval_status)
                self.check_sensitive_data_removed_from_database(applicant_approval)
                self.check_essay_hidden_from_application_review(account=reviewer.account, applicant_approval=applicant_approval)
                self.check_race_and_ethnicity_hidden_from_application_review(account=reviewer.account, applicant_approval=applicant_approval)
                self.check_gender_identity_hidden_from_application_review(account=reviewer.account, applicant_approval=applicant_approval)

                # Make sure all linked essay qualities have been cleared.
                # Empty lists (or empty query sets) evaluate to False.
                self.assertFalse(applicant_approval.essay_qualities.all())

                applicant_approval.delete()

                # Ensure the essay quality objects remain in the database,
                # even after the application that had a foreign key reference
                # to the essay quality gets deleted
                models.EssayQuality.objects.get(
                        category=essay_quality.category,
                        description=essay_quality.description,
                        )
Beispiel #11
0
 def setup_approved_project_approved_community(self, start_from):
     current_round = factories.RoundPageFactory(start_from=start_from)
     project = factories.ProjectFactory(
             approval_status=models.ApprovalStatus.APPROVED,
             project_round__participating_round=current_round,
             project_round__approval_status=models.ApprovalStatus.APPROVED,
             project_round__community__name='Debian',
             project_round__community__slug='debian',
             contribution_tasks='<p>Just pick something from the issue tracker.</p>',
             )
     sponsorship = factories.SponsorshipFactory(participation=project.project_round,
             name='Software in the Public Interest - Debian',
             amount=13000)
     return project
Beispiel #12
0
    def test_applicant_prompts_approved_after_contributions_open(self):
        """
        This tests the applicant prompts in combination with the initial application results.
        The applicant is approved.
        The round is in the contribution period.
        /dashboard/ should show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='contributions_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.APPROVED, application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('dashboard'))
        self.assertContains(response, '<div class="card-header text-white bg-warning">A Contribution is Required</div>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #13
0
    def test_applicant_prompts_time_rejection_after_contributions_open(self):
        """
        This tests that the initial application results.
        The applicant is rejected because of 'TIME' - they don't have 49 out of 91 days free from full-time commitments.
        The round is in the initial application period.
        /dashboard/ should show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='contributions_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.REJECTED, reason_denied='TIME', application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('dashboard'))
        self.assertContains(response, '<div class="card-header text-white bg-warning">Initial Application Not Approved</div>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #14
0
    def test_approved_reviewers_can_see_sensitive_data(self):
        """
        This tests that approved applicant reviewers can see applicant essay answers after the initial application was submitted.
        The round is in the initial application period.
        /applicant-review-detail/ should show the essay answers.
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')
        applicant_approval = self.create_initial_application(current_round)

        reviewer = self.create_applicant_reviewer(current_round, models.ApprovalStatus.APPROVED)
        self.client.force_login(reviewer.account)

        self.check_essay_visible_on_application_review(account=reviewer.account, applicant_approval=applicant_approval)
        self.check_race_and_ethnicity_visible_on_application_review(account=reviewer.account, applicant_approval=applicant_approval)
        self.check_gender_identity_visible_on_application_review(account=reviewer.account, applicant_approval=applicant_approval)
Beispiel #15
0
    def test_initial_application_results_pending_after_contributions_open(self):
        """
        This tests that the initial application results.
        The applicant was not reviewed, either because they didn't answer our emails, or we didn't have time to review all applications.
        The round is in the contribution application period.
        /eligibility-results/ should show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='contributions_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.PENDING, application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('eligibility-results'))
        self.assertNotContains(response, '<h1>Your Initial Application is Under Review</h1>', html=True)
        self.assertContains(response, '<h1>Initial application is not approved</h1>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #16
0
    def test_initial_application_results_approved_after_contributions_open(self):
        """
        This tests that the initial application results.
        The applicant is approved.
        The round is in the contribution period.
        /eligibility-results/ should show the person is approved.
        """
        current_round = factories.RoundPageFactory(start_from='contributions_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.APPROVED, application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('eligibility-results'))
        self.assertNotContains(response, '<h1>Your Initial Application is Under Review</h1>', html=True)
        self.assertContains(response, '<h1>Initial application approved for Outreachy</h1>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #17
0
    def test_initial_application_results_alignment_rejection_after_contributions_open(self):
        """
        This tests that the initial application results.
        The applicant is rejected because of 'ALIGN' - mis-alignment with Outreachy program goals.
        The round is in the contribution application period.
        /eligibility-results/ should show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='contributions_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.REJECTED, reason_denied='ALIGN', application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('eligibility-results'))
        self.assertNotContains(response, '<h1>Your Initial Application is Under Review</h1>', html=True)
        self.assertContains(response, '<h1>Initial application is not approved</h1>', html=True)
        self.assertContains(response, '<p>The Outreachy organizers have been reviewing your initial application, including your essay questions.</p>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #18
0
    def test_initial_application_results_general_rejection(self):
        """
        This tests that the initial application results.
        The applicant is rejected because of 'GENERAL' - they don't meet our eligibility rules
        The round is in the initial application period.
        /eligibility-results/ should not show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.REJECTED, reason_denied='GENERAL', application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('eligibility-results'))
        self.assertContains(response, '<h1>Your Initial Application is Under Review</h1>', html=True)
        self.assertNotContains(response, '<h1>Initial application is not approved</h1>', html=True)
        self.assertNotContains(response, '<li>You may have already participated in Google Summer of Code or a previous Outreachy round.</li>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #19
0
    def test_initial_application_results_time_rejection(self):
        """
        This tests that the initial application results.
        The applicant is rejected because of 'TIME' - they don't have 49 out of 91 days free from full-time commitments.
        The round is in the initial application period.
        /eligibility-results/ should not show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')
        applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.REJECTED, reason_denied='TIME', application_round=current_round)
        self.client.force_login(applicant.applicant.account)

        response = self.client.get(reverse('eligibility-results'))
        self.assertContains(response, '<h1>Your Initial Application is Under Review</h1>', html=True)
        self.assertNotContains(response, '<h1>Initial application is not approved</h1>', html=True)
        self.assertNotContains(response, '<p>After reviewing your time commitments, we have determined you do not meet our minimum free time criteria.</p>', html=True)
        self.assertEqual(response.status_code, 200)
Beispiel #20
0
    def test_applicant_prompts_alignment_time_general_rejection_before_contributions_open(self):
        """
        This tests that the initial application results.
        The applicant is rejected because of 'ALIGN' - mis-alignment with Outreachy program goals.
        The round is in the initial application period.
        /dashboard/ should NOT show the person is rejected.
        """
        current_round = factories.RoundPageFactory(start_from='initial_applications_open')
        for rejection_reason in ['GENERAL', 'TIME', 'ALIGN', ]:
            with self.subTest(rejection_reason=rejection_reason):
                applicant = factories.ApplicantApprovalFactory(approval_status=models.ApprovalStatus.REJECTED, reason_denied=rejection_reason, application_round=current_round)
                self.client.force_login(applicant.applicant.account)

                response = self.client.get(reverse('dashboard'))
                self.assertNotContains(response, '<div class="card-header text-white bg-warning">Initial Application Not Approved</div>', html=True)
                self.assertContains(response, '<div class="card-header text-white bg-warning">Outreachy Initial Application Under Review</div>', html=True)
                self.assertEqual(response.status_code, 200)
    def test_community_read_only_submission_text_cfp_open_uncertain_participation(
            self):
        """
        This tests how the page for coordinators and mentors of a community
        looks after a new round has been announced,
        but before the community signs up to participate.

        Test home/templates/home/community_read_only.html:
         - Create a community that has been approved to participate in a past round
         - Create a new RoundPage for the upcoming round
         - Check:
           - The 'Not Participating' status is visible
           - The 'Coordinate for This Community' button is visible to anyone who is not a coordinator
           - The 'Community will participate' button is visible to a coordinator
           - The 'Community will not participate' button is visible to a coordinator
           - The 'Submit a Project Proposal' button is not visible
           - The 'Submit an Outreachy Intern Project Proposal' heading is not visible
        """
        scenario = scenarios.InternshipWeekScenario(week=10,
                                                    community__name='Debian',
                                                    community__slug='debian')
        community_read_only_path = reverse(
            'community-read-only',
            kwargs={
                'community_slug': scenario.participation.community.slug,
            })
        current_round = factories.RoundPageFactory(start_from='pingnew')

        project_submission_path = reverse(
            'project-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        coordinator_signup_path = reverse(
            'coordinatorapproval-action',
            kwargs={
                'action': 'submit',
                'community_slug': scenario.participation.community.slug,
            })
        community_does_participate_path = reverse(
            'participation-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        community_does_not_participate_path = reverse(
            'participation-action',
            kwargs={
                'action': 'withdraw',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })

        visitors = self.get_visitors_from_past_round(scenario)

        for visitor_type, visitor in visitors:
            with self.subTest(visitor_type=visitor_type):
                self.client.logout()
                if visitor:
                    self.client.force_login(visitor)
                response = self.client.get(community_read_only_path)
                self.assertEqual(response.status_code, 200)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-warning">Not Participating</span>',
                    html=True)
                if visitor_type != 'coordinator':
                    self.assertContains(
                        response,
                        '<a href="{}" class="btn btn-success">Coordinate for This Community</a>'
                        .format(coordinator_signup_path),
                        html=True)
                else:
                    self.assertContains(
                        response,
                        '<a href="{}" class="btn btn-success">Community will participate</a>'
                        .format(community_does_participate_path),
                        html=True)
                    self.assertContains(
                        response,
                        '<a href="{}" class="btn btn-warning">Community will not participate</a>'
                        .format(community_does_not_participate_path),
                        html=True)
                self.assertNotContains(
                    response,
                    '<h2>Submit an Outreachy Intern Project Proposal</h2>',
                    html=True)
                self.assertNotContains(
                    response,
                    '<a class="btn btn-success" href="{}">Submit a Project Proposal</a>'
                    .format(project_submission_path),
                    html=True)
    def test_project_hard_deadline(self):
        """
        This tests submitting a project after the deadline for project approval fails:
         - Create a new RoundPage for the upcoming round with the project submission deadline passed
         - Create an approved community
         - Go to the community read-only page
         - There should not be a 'Submit Project' button
         - Submitting a project directly via the URL should fail
        """
        scenario = scenarios.InternshipWeekScenario(week=15,
                                                    community__name='Debian',
                                                    community__slug='debian')

        current_round = factories.RoundPageFactory(start_from='lateprojects')
        participation = factories.ParticipationFactory(
            community=scenario.community,
            participating_round=current_round,
            approval_status=ApprovalStatus.APPROVED)

        # Check community CFP page to ensure the message about the CFP being closed is displayed
        response = self.client.get(reverse('community-cfp'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(
            response,
            '<div class="card-header text-white bg-warning">Project and community CFP is currently closed</div>',
            html=True)

        # Check community CFP page to ensure the message about the CFP being closed is displayed
        response = self.client.get(
            reverse('community-read-only',
                    kwargs={
                        'community_slug':
                        scenario.participation.community.slug,
                    }))
        self.assertContains(
            response,
            '<div class="card-header text-white bg-warning">Project and community CFP is currently closed</div>',
            html=True)

        # Check that there is not a submit project button
        self.assertNotContains(
            response,
            '<h2>Submit an Outreachy Intern Project Proposal</h2>',
            html=True)
        self.assertContains(response,
                            '<h2>Project Submission Deadline Passed</h2>',
                            html=True)
        project_submission_path = reverse(
            'project-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        self.assertNotContains(
            response,
            '<a class="btn btn-success" href="{}">Submit a Project Proposal</a>'
            .format(project_submission_path),
            html=True)

        sponsorship = factories.SponsorshipFactory(
            participation=participation,
            name='Software in the Public Interest - Debian',
            amount=13000)
        visitors = self.get_visitors_from_past_round(scenario)

        # Submit project description
        self.client.force_login(scenario.mentor.account)
        response = self.client.post(
            project_submission_path,
            {
                'approved_license': 'on',
                'no_proprietary_software': 'on',
                'longevity': '2Y',
                'community_size': '20',
                'short_title':
                'Improve Debian bioinformatics packages test coverage',
                'long_description':
                'The Debian Med project has packaged a lot of <a href="http://blends.debian.org/med/tasks/bio">applications for bioinformatics</a>. You will be improving the test coverage of those packages.',
                'minimum_system_requirements': 'A system running Debian Linux',
                'contribution_tasks':
                'Look at issues marked newcomers-welcome.',
                'repository': 'https://salsa.debian.org/med-team',
                'issue_tracker': 'https://bugs.debian.org/',
                'newcomer_issue_tag': 'newcomers-welcome',
                'intern_tasks':
                'Interns will work on new tests for <a href="http://blends.debian.org/med/tasks/bio">Debian bioinformatics packages</a>.',
                'intern_benefits':
                'Interns will develop skills in quality assurance testing, learn Linux command-line tools, and gain knowledge in how Linux distributions like Debian package software.',
                'community_benefits':
                'Debian maintainers will spend less time tracking down bugs in newly released software.',
                'new_contributors_welcome': True,
            },
            # This says we're supposed to follow any and all redirects to other pages after the post
            # This will allow us to record a history of where the redirect went to
            follow=True,
        )
        self.assertEqual(response.status_code, 403)
        self.assertContains(
            response,
            'Not allowed to submit a project after its submission and approval deadline ({})'
            .format(current_round.lateprojects),
            status_code=403)
    def test_project_soft_deadline(self):
        """
        Mentors are told that projects are due one week before they're actually due.
        This is a "soft" project submission deadline.
        It's seven days before lateprojects - can be found by calling RoundPage.project_soft_deadline().

        After the soft project submission deadline,
        the community CFP page shows project submission is closed.
        The community read-only page doesn't show the submit project button.
        *But* mentors can still submit if they know the URL.
        This allows us to deal with the few mentors who always miss the deadline.

        This tests submitting a project after the soft deadline succeeds:
         - Create a new RoundPage for the upcoming round with the project submission deadline passed
         - Create an approved community
         - Go to the community read-only page
         - There should not be a 'Submit Project' button
         - Submitting a project directly via the URL should still work
        """
        scenario = scenarios.InternshipWeekScenario(week=14,
                                                    community__name='Debian',
                                                    community__slug='debian')

        current_round = factories.RoundPageFactory(start_from='lateprojects',
                                                   days_after_today=6)
        participation = factories.ParticipationFactory(
            community=scenario.community,
            participating_round=current_round,
            approval_status=ApprovalStatus.APPROVED)

        # Check community CFP page to ensure the message about the CFP being closed is displayed
        response = self.client.get(reverse('community-cfp'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(
            response,
            '<div class="card-header text-white bg-warning">Project and community CFP is currently closed</div>',
            html=True)

        # Check community CFP page to ensure the message about the CFP being closed is displayed
        response = self.client.get(
            reverse('community-read-only',
                    kwargs={
                        'community_slug':
                        scenario.participation.community.slug,
                    }))
        self.assertContains(
            response,
            '<div class="card-header text-white bg-warning">Project and community CFP is currently closed</div>',
            html=True)

        # Check that there is not a submit project button
        self.assertNotContains(
            response,
            '<h2>Submit an Outreachy Intern Project Proposal</h2>',
            html=True)
        self.assertContains(response,
                            '<h2>Project Submission Deadline Passed</h2>',
                            html=True)
        project_submission_path = reverse(
            'project-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        self.assertNotContains(
            response,
            '<a class="btn btn-success" href="{}">Submit a Project Proposal</a>'
            .format(project_submission_path),
            html=True)

        self.check_project_submission(scenario, current_round, participation)
    def test_project_submission_no_community_participation(self):
        """
        This tests submitting a project when the community is not signed up to participate
         - Create a new RoundPage for the upcoming round with project submission open
         - Do not add the old community to participate in the round
         - Go to the community read-only page
         - There should not be a 'Submit Project' button
         - Submitting a project directly via the URL should fail
        """
        scenario = scenarios.InternshipWeekScenario(week=10,
                                                    community__name='Debian',
                                                    community__slug='debian')

        current_round = factories.RoundPageFactory(start_from='pingnew')

        # Check community CFP page to ensure the message about the CFP being closed is displayed
        response = self.client.get(reverse('community-cfp'))
        self.assertEqual(response.status_code, 200)

        # Check community CFP page to ensure the message about the CFP being closed is displayed
        response = self.client.get(
            reverse('community-read-only',
                    kwargs={
                        'community_slug':
                        scenario.participation.community.slug,
                    }))
        self.assertContains(
            response,
            '<span class="badge badge-pill badge-warning">Not Participating</span>',
            html=True)

        # Check that there is not a submit project button
        self.assertNotContains(
            response,
            '<h2>Submit an Outreachy Intern Project Proposal</h2>',
            html=True)
        project_submission_path = reverse(
            'project-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        self.assertNotContains(
            response,
            '<a class="btn btn-success" href="{}">Submit a Project Proposal</a>'
            .format(project_submission_path),
            html=True)

        # Submit project description
        self.client.force_login(scenario.mentor.account)
        response = self.client.post(
            project_submission_path,
            {
                'approved_license': 'on',
                'no_proprietary_software': 'on',
                'longevity': '2Y',
                'community_size': '20',
                'short_title':
                'Improve Debian bioinformatics packages test coverage',
                'long_description':
                'The Debian Med project has packaged a lot of <a href="http://blends.debian.org/med/tasks/bio">applications for bioinformatics</a>. You will be improving the test coverage of those packages.',
                'minimum_system_requirements': 'A system running Debian Linux',
                'contribution_tasks':
                'Look at issues marked newcomers-welcome.',
                'repository': 'https://salsa.debian.org/med-team',
                'issue_tracker': 'https://bugs.debian.org/',
                'newcomer_issue_tag': 'newcomers-welcome',
                'intern_tasks':
                'Interns will work on new tests for <a href="http://blends.debian.org/med/tasks/bio">Debian bioinformatics packages</a>.',
                'intern_benefits':
                'Interns will develop skills in quality assurance testing, learn Linux command-line tools, and gain knowledge in how Linux distributions like Debian package software.',
                'community_benefits':
                'Debian maintainers will spend less time tracking down bugs in newly released software.',
                'new_contributors_welcome': True,
            },
            # This says we're supposed to follow any and all redirects to other pages after the post
            # This will allow us to record a history of where the redirect went to
            follow=True,
        )
        self.assertEqual(response.status_code, 404)
Beispiel #25
0
    def test_community_participation_approval(self):
        """
        This tests approving a community to participate in this round.
         - Create a new RoundPage for the upcoming round, with a pending community
         - Go to the community read-only page
         - Log in as an organizer
         - The community read-only page should have an 'Approve Community' and a 'Reject Community' button
         - Post to the Participation approval URL
         - This should redirect back to community read-only page
         - Participation should now marked as approved in the database
         - Coordinator receives email that the community was approved to participate
         - The community read-only page should now reflect that the community has been approved
           - Community status box should read 'Participating'
         - There should still be a way to submit projects

        Test home/templates/home/community_read_only.html:
         - Check:
           - The 'Participating' status is visible
           - Funding for 1 intern is visible
           - The 'Coordinate for This Community' button is visible to anyone who is not a coordinator
           - The 'Submit a Project Proposal' button is visible
           - The 'Submit an Outreachy Intern Project Proposal' heading is visible
           - The 'Community will participate' button is visible to a coordinator
           - The 'Community will not participate' button is visible to a coordinator
        """
        scenario = scenarios.InternshipWeekScenario(week=10,
                                                    community__name='Debian',
                                                    community__slug='debian')
        current_round = factories.RoundPageFactory(start_from='pingnew')

        community_read_only_path = reverse(
            'community-read-only',
            kwargs={
                'community_slug': scenario.participation.community.slug,
            })
        project_submission_path = reverse(
            'project-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        coordinator_signup_path = reverse(
            'coordinatorapproval-action',
            kwargs={
                'action': 'submit',
                'community_slug': scenario.participation.community.slug,
            })
        community_does_participate_path = reverse(
            'participation-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        approve_participation_path = reverse(
            'participation-action',
            kwargs={
                'action': 'approve',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        reject_participation_path = reverse(
            'participation-action',
            kwargs={
                'action': 'reject',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        visitors = self.get_visitors_from_past_round(scenario)

        # Set up the community with a pending participation in the current round
        participation = factories.ParticipationFactory(
            community=scenario.community,
            participating_round=current_round,
            approval_status=ApprovalStatus.PENDING)
        sponsorship = factories.SponsorshipFactory(
            participation=participation,
            name='Software in the Public Interest - Debian',
            amount=13000)

        organizer_account = User.objects.get(is_staff=True)
        self.client.force_login(organizer_account)

        # Double check that the community read-only page has links to approve or reject
        response = self.client.get(community_read_only_path)
        self.assertEqual(response.status_code, 200)
        self.assertContains(
            response,
            '<span class="badge badge-pill badge-info">Pending Participation</span>',
            html=True)
        self.assertContains(
            response,
            '<input type="submit" class="btn btn-success m-2" value="Approve Community" />',
            html=True)
        self.assertContains(
            response,
            '<a href="{}" class="btn btn-warning m-2">Reject Community</a>'.
            format(reject_participation_path),
            html=True)

        # Approve the community
        response = self.client.post(approve_participation_path)
        self.assertEqual(response.status_code, 302)

        # Check the database status
        approved_participation = Participation.objects.get(
            community__slug=participation.community.slug,
            participating_round__slug=current_round.slug,
            approval_status=ApprovalStatus.APPROVED)

        # Check that the email to the community coordinator was sent
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(
            mail.outbox[0].subject, '{} is participating in Outreachy!'.format(
                scenario.community.name))
        self.assertEqual(mail.outbox[0].from_email, organizers)
        self.assertEqual(mail.outbox[0].to,
                         scenario.community.get_coordinator_email_list())
        self.assertIn(
            'The Outreachy organizers have approved {} to participate in the current round of Outreachy!'
            .format(scenario.community.name), mail.outbox[0].body)
        # TODO: we should probably check that other information is correct,
        # like the round dates, but this is enough for now.

        # Check that the community read-only page reflects the database status
        for visitor_type, visitor in visitors:
            with self.subTest(visitor_type=visitor_type):
                self.client.logout()
                if visitor:
                    self.client.force_login(visitor)
                response = self.client.get(community_read_only_path)
                self.assertEqual(response.status_code, 200)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-success">Participating</span>',
                    html=True)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-success">Funded</span>',
                    html=True)
                self.assertContains(
                    response,
                    '<td>This community has funding for 2 interns.</td>',
                    html=True)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-warning">No Projects</span>',
                    html=True)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-info">Open to New Projects</span>',
                    html=True)
                if visitor_type != 'coordinator':
                    self.assertContains(
                        response,
                        '<a href="{}" class="btn btn-success">Coordinate for This Community</a>'
                        .format(coordinator_signup_path),
                        html=True)
                self.assertContains(
                    response,
                    '<h2>Submit an Outreachy Intern Project Proposal</h2>',
                    html=True)
                self.assertContains(
                    response,
                    '<a class="btn btn-success" href="{}">Submit a Project Proposal</a>'
                    .format(project_submission_path),
                    html=True)
Beispiel #26
0
    def test_old_community_participation_signup(self):
        """
        This tests submitting an older community to participate in this round.
         - Create a community that has been approved to participate in a past round
         - Create a new RoundPage for the upcoming round
         - Submit the community to participate in the round through the form
         - There should be an email sent to the Outreachy organizers about the participation
         - There should be a Participation object for this community in the current round marked as PENDING

        Test home/templates/home/community_read_only.html:
         - Check:
           - The 'Pending Participation' status is visible
           - Funding for 2 interns is visible
           - The 'Coordinate for This Community' button is visible to anyone who is not a coordinator
           - The 'Submit a Project Proposal' button is visible
           - The 'Submit an Outreachy Intern Project Proposal' heading is visible
           - The 'Community will participate' button is visible to a coordinator
           - The 'Community will not participate' button is visible to a coordinator
        """
        scenario = scenarios.InternshipWeekScenario(week=10,
                                                    community__name='Debian',
                                                    community__slug='debian')
        current_round = factories.RoundPageFactory(start_from='pingnew')

        community_read_only_path = reverse(
            'community-read-only',
            kwargs={
                'community_slug': scenario.participation.community.slug,
            })
        project_submission_path = reverse(
            'project-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        coordinator_signup_path = reverse(
            'coordinatorapproval-action',
            kwargs={
                'action': 'submit',
                'community_slug': scenario.participation.community.slug,
            })
        community_does_participate_path = reverse(
            'participation-action',
            kwargs={
                'action': 'submit',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        community_does_not_participate_path = reverse(
            'participation-action',
            kwargs={
                'action': 'withdraw',
                'round_slug': current_round.slug,
                'community_slug': scenario.participation.community.slug,
            })
        sponsor_name = 'Software in the Public Interest - Debian'
        sponsor_amount = 13000

        visitors = self.get_visitors_from_past_round(scenario)
        # There should not be a Participation for Debian in the current round yet
        with self.assertRaises(Participation.DoesNotExist):
            p = Participation.objects.get(
                community__slug=scenario.participation.community.slug,
                participating_round__slug=current_round.slug)

        self.client.force_login(scenario.coordinator.account)
        response = self.coordinator_signs_up_community_to_participate(
            reverse('participation-action',
                    kwargs={
                        'action': 'submit',
                        'round_slug': current_round.slug,
                        'community_slug':
                        scenario.participation.community.slug,
                    }),
            sponsor_name,
            sponsor_amount,
        )
        self.assertEqual(response.status_code, 302)

        # Ensure the database reflects the community sign-up
        participation = Participation.objects.get(
            community__slug=scenario.participation.community.slug,
            participating_round__slug=current_round.slug,
            approval_status=ApprovalStatus.PENDING)
        sponsorship = Sponsorship.objects.get(participation=participation,
                                              coordinator_can_update=True,
                                              name=sponsor_name,
                                              amount=sponsor_amount,
                                              funding_secured=True)

        # Make sure the email to the Outreachy organizers was sent
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject,
                         'Approve community participation - Debian')
        self.assertEqual(mail.outbox[0].from_email, organizers)
        self.assertEqual(mail.outbox[0].to, [organizers])
        self.assertIn(community_read_only_path, mail.outbox[0].body)
        self.assertIn('Number of interns funded: 2', mail.outbox[0].body)
        self.assertIn(sponsor_name, mail.outbox[0].body)
        self.assertIn(str(sponsor_amount), mail.outbox[0].body)

        for visitor_type, visitor in visitors:
            with self.subTest(visitor_type=visitor_type):
                self.client.logout()
                if visitor:
                    self.client.force_login(visitor)
                response = self.client.get(community_read_only_path)
                self.assertEqual(response.status_code, 200)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-info">Pending Participation</span>',
                    html=True)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-success">Funded</span>',
                    html=True)
                self.assertContains(
                    response,
                    '<td>This community has funding for 2 interns.</td>',
                    html=True)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-warning">No Projects</span>',
                    html=True)
                self.assertContains(
                    response,
                    '<span class="badge badge-pill badge-info">Open to New Projects</span>',
                    html=True)
                if visitor_type != 'coordinator':
                    self.assertContains(
                        response,
                        '<a href="{}" class="btn btn-success">Coordinate for This Community</a>'
                        .format(coordinator_signup_path),
                        html=True)
                self.assertContains(
                    response,
                    '<h2>Submit an Outreachy Intern Project Proposal</h2>',
                    html=True)
                self.assertContains(
                    response,
                    '<a class="btn btn-success" href="{}">Submit a Project Proposal</a>'
                    .format(project_submission_path),
                    html=True)