Skip to content

Commit

Permalink
Merge pull request #20948 from code-dot-org/dtl_candidate_de30436e
Browse files Browse the repository at this point in the history
  • Loading branch information
deploy-code-org committed Feb 28, 2018
2 parents 93f93a7 + de30436 commit f6bad97
Show file tree
Hide file tree
Showing 195 changed files with 2,425 additions and 6,437 deletions.
1 change: 1 addition & 0 deletions apps/src/code-studio/pd/permission.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class Permission {
// CSF Facilitators can create workshops, other facilitators cannot
this.isCsfFacilitator = this.hasPermission('csf_facilitator');
this.isOrganizer = this.hasPermission('workshop_organizer');
this.isProgramManager = this.hasPermission('program_manager');
this.isPartner = this.hasPermission('partner');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ export default class WorkshopForm extends React.Component {
style={this.getInputStyle()}
value={this.state.regional_partner_id || ''}
// Facilitators (who are not organizers, partners, nor admins) cannot edit this field
disabled={this.props.readOnly || (!this.permission.isWorkshopAdmin && !this.permission.isOrganizer && !this.permission.isPartner)}
disabled={this.props.readOnly || (!this.permission.isWorkshopAdmin && !this.permission.isOrganizer && !this.permission.isProgramManager && !this.permission.isPartner)}
>
<option value="">None</option>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default class WorkshopManagement extends React.Component {
if (this.props.subject === '5-day Summer') {
surveyBaseUrl = "local_summer_workshop_survey_results";
} else {
surveyBaseUrl = this.permission.isOrganizer ? "organizer_survey_results" : "survey_results";
surveyBaseUrl = (this.permission.isOrganizer || this.permission.isProgramManager) ? "organizer_survey_results" : "survey_results";
}

this.surveyUrl = `/${surveyBaseUrl}/${this.props.workshopId}`;
Expand Down
6 changes: 3 additions & 3 deletions apps/src/code-studio/pd/workshop_dashboard/workshop_index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export default class WorkshopIndex extends React.Component {

render() {
const showOrganizer = this.permission.isWorkshopAdmin;
const canDelete = this.permission.isWorkshopAdmin || this.permission.isOrganizer;
const canCreate = (this.permission.isWorkshopAdmin || this.permission.isOrganizer || this.permission.isCsfFacilitator);
const canDelete = this.permission.isWorkshopAdmin || this.permission.isOrganizer || this.permission.isProgramManager;
const canCreate = (this.permission.isWorkshopAdmin || this.permission.isOrganizer || this.permission.isProgramManager || this.permission.isCsfFacilitator);

return (
<div>
Expand All @@ -79,7 +79,7 @@ export default class WorkshopIndex extends React.Component {
</Button>
)
}
{(this.permission.isWorkshopAdmin || this.permission.isOrganizer) && <Button onClick={this.handleAttendanceReportsClick}>Attendance Reports</Button>}
{(this.permission.isWorkshopAdmin || this.permission.isOrganizer || this.permission.isProgramManager) && <Button onClick={this.handleAttendanceReportsClick}>Attendance Reports</Button>}
{this.permission.isPartner && <Button onClick={this.handleOrganizerSurveyResultsClick}>Organizer Survey Results</Button>}
{this.permission.isFacilitator && <Button onClick={this.handleSurveyResultsClick}>Facilitator Survey Results</Button>}
<Button
Expand Down
25 changes: 25 additions & 0 deletions apps/test/unit/code-studio/pd/permissionTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ describe("Permission class", () => {
expect(permission.isWorkshopAdmin).to.be.true;
expect(permission.isFacilitator).to.be.false;
expect(permission.isOrganizer).to.be.false;
expect(permission.isProgramManager).to.be.false;
expect(permission.isPartner).to.be.false;
});

Expand All @@ -34,6 +35,17 @@ describe("Permission class", () => {
expect(permission.isWorkshopAdmin).to.be.false;
expect(permission.isFacilitator).to.be.false;
expect(permission.isOrganizer).to.be.true;
expect(permission.isProgramManager).to.be.false;
expect(permission.isPartner).to.be.false;
});

it("Detects program manager", () => {
setGlobalPermissionString("[program_manager]");
const permission = new Permission();
expect(permission.isWorkshopAdmin).to.be.false;
expect(permission.isFacilitator).to.be.false;
expect(permission.isOrganizer).to.be.false;
expect(permission.isProgramManager).to.be.true;
expect(permission.isPartner).to.be.false;
});

Expand All @@ -43,6 +55,7 @@ describe("Permission class", () => {
expect(permission.isWorkshopAdmin).to.be.false;
expect(permission.isFacilitator).to.be.false;
expect(permission.isOrganizer).to.be.false;
expect(permission.isProgramManager).to.be.false;
expect(permission.isPartner).to.be.true;
});

Expand All @@ -52,6 +65,7 @@ describe("Permission class", () => {
expect(permission.isWorkshopAdmin).to.be.false;
expect(permission.isFacilitator).to.be.false;
expect(permission.isOrganizer).to.be.true;
expect(permission.isProgramManager).to.be.false;
expect(permission.isPartner).to.be.true;
});

Expand All @@ -61,6 +75,17 @@ describe("Permission class", () => {
expect(permission.isWorkshopAdmin).to.be.false;
expect(permission.isFacilitator).to.be.true;
expect(permission.isOrganizer).to.be.true;
expect(permission.isProgramManager).to.be.false;
expect(permission.isPartner).to.be.false;
});

it("Detects multiple permissions for organizer-program_managers", () => {
setGlobalPermissionString("[program_manager,workshop_organizer]");
const permission = new Permission();
expect(permission.isWorkshopAdmin).to.be.false;
expect(permission.isFacilitator).to.be.false;
expect(permission.isOrganizer).to.be.true;
expect(permission.isProgramManager).to.be.true;
expect(permission.isPartner).to.be.false;
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,38 @@ describe("WorkshopManagement", () => {
});
});

describe("For completed workshops to a program manager organizer", () => {
beforeEach(() => {
(new Permission()).setPermission("program_manager");
mockRouter.expects("createHref").withExactArgs("viewUrl").returns("viewHref");
mockRouter.expects("createHref").withExactArgs("/organizer_survey_results/123").returns("organizerResultsHref");

workshopManagement = shallow(
<WorkshopManagement
workshopId = {123}
viewUrl = "viewUrl"
showSurveyUrl={true}
/>,
{context}
);
});

it("Has 2 buttons", () => {
expect(findButtons()).to.have.length(2);
});

it("Has a view workshop button", () => {
verifyViewWorkshopButton();
});

it("Has a View Survey Results button", () => {
const surveyButton = findButtonWithText("View Survey Results");
expect(surveyButton.props().href).to.eql("organizerResultsHref");

mockRouter.expects("push").withExactArgs("/organizer_survey_results/123");
surveyButton.simulate('click', mockClickEvent);
});
});

describe("For completed workshops to a facilitator", () => {
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ describe("WorkshopIndex", () => {
"Filter View"
]
],
[
"program_manager",
[
"New Workshop",
"Attendance Reports",
"Filter View"
]
],
[
"workshop_admin",
[
Expand Down
10 changes: 10 additions & 0 deletions aws/redshift/schemas.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ TO GROUP reader, GROUP reader_pii, GROUP admin;
GRANT USAGE
ON SCHEMA analysis_pii
TO GROUP reader_pii, GROUP admin;

ALTER DEFAULT PRIVILEGES
IN SCHEMA analysis
GRANT SELECT ON TABLES
TO GROUP reader, GROUP reader_pii;

ALTER DEFAULT PRIVILEGES
IN SCHEMA analysis_pii
GRANT SELECT ON TABLES
TO GROUP reader_pii;
19 changes: 19 additions & 0 deletions aws/redshift/tables/csf_plugged_stage_counts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
create table analysis.csf_plugged_stage_counts (
script_id int,
plugged_stage_counts int
);

insert into analysis.csf_plugged_stage_counts values
(1,9),
(17,11),
(18,11),
(19,14),
(23,16),
(236,5),
(237,6),
(238,9),
(239,12),
(240,8),
(241,10),
(258,19),
(259,7);
24 changes: 20 additions & 4 deletions aws/redshift/tables/school_activity_stats.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@ CREATE table analysis.school_activity_stats AS
-- By school_id, has a school pledged to expand computer science?
select distinct si.school_id
from dashboard_production_pii.census_submissions cs
join dashboard_production_pii.census_submissions_school_infos cssi
join dashboard_production.census_submissions_school_infos cssi
on cssi.census_submission_id = cs.id
join dashboard_production.school_infos si
on si.id = cssi.school_info_id
where pledged = 1
),
hoc_event as (
-- by school ID, did a school host an hour of code?
select distinct json_extract_path_text(data, 'nces_school_s') school_id
from forms
where kind = 'HocSignup2017'
and json_extract_path_text(data, 'nces_school_s') not in ('','-1')
)
-- Schools in geographies where a regional partner is mapped to a zip code
SELECT ss.school_id,
Expand Down Expand Up @@ -54,7 +61,8 @@ CREATE table analysis.school_activity_stats AS
COUNT(DISTINCT CASE WHEN se.script_id IN (122,123,124,125,126,127) THEN f.student_user_id ELSE NULL END) students_csp,
COUNT(DISTINCT CASE WHEN csf_pd.user_id IS NOT NULL THEN u.id ELSE NULL END) teachers_csf_pd,
COUNT(DISTINCT CASE WHEN scr.name IN ('starwars','starwarsblocks','mc','minecraft','hourofcode','flappy','artist','frozen','infinity','playlab','gumball','iceage','sports','basketball') THEN f.student_user_id ELSE NULL END) students_hoc,
MAX(CASE WHEN pledged.school_id is not null then 1 else 0 end) pledged
MAX(CASE WHEN pledged.school_id is not null then 1 end) pledged,
MAX(CASE WHEN hoc_event.school_id is not null then 1 end) hoc_event
FROM analysis.school_stats ss
LEFT JOIN dashboard_production.schools sc on sc.id = ss.school_id
LEFT JOIN dashboard_production.school_infos si
Expand All @@ -79,6 +87,8 @@ CREATE table analysis.school_activity_stats AS
ON csf_pd.user_id = u.id
LEFT JOIN pledged
ON pledged.school_id = ss.school_id
LEFT JOIN hoc_event
ON hoc_event.school_id = ss.school_id
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14

union all
Expand Down Expand Up @@ -109,7 +119,8 @@ union all
COUNT(DISTINCT CASE WHEN se.script_id IN (122,123,124,125,126,127) THEN f.student_user_id ELSE NULL END) students_csp,
COUNT(DISTINCT CASE WHEN csf_pd.user_id IS NOT NULL THEN u.id ELSE NULL END) teachers_csf_pd,
COUNT(DISTINCT CASE WHEN scr.name IN ('starwars','starwarsblocks','mc','minecraft','hourofcode','flappy','artist','frozen','infinity','playlab','gumball','iceage','sports','basketball') THEN f.student_user_id ELSE NULL END) students_hoc,
MAX(CASE WHEN pledged.school_id is not null then 1 else 0 end) pledged
MAX(CASE WHEN pledged.school_id is not null then 1 end) pledged,
MAX(CASE WHEN hoc_event.school_id is not null then 1 end) hoc_event
FROM analysis.school_stats ss
LEFT JOIN dashboard_production.schools sc on sc.id = ss.school_id
LEFT JOIN dashboard_production.school_infos si
Expand All @@ -134,6 +145,8 @@ union all
ON csf_pd.user_id = u.id
LEFT JOIN pledged
ON pledged.school_id = ss.school_id
LEFT JOIN hoc_event
ON hoc_event.school_id = ss.school_id
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14

union all
Expand Down Expand Up @@ -164,7 +177,8 @@ union all
COUNT(DISTINCT CASE WHEN se.script_id IN (122,123,124,125,126,127) THEN f.student_user_id ELSE NULL END) students_csp,
COUNT(DISTINCT CASE WHEN csf_pd.user_id IS NOT NULL THEN u.id ELSE NULL END) teachers_csf_pd,
COUNT(DISTINCT CASE WHEN scr.name IN ('starwars','starwarsblocks','mc','minecraft','hourofcode','flappy','artist','frozen','infinity','playlab','gumball','iceage','sports','basketball') THEN f.student_user_id ELSE NULL END) students_hoc,
MAX(CASE WHEN pledged.school_id is not null then 1 else 0 end) pledged
MAX(CASE WHEN pledged.school_id is not null then 1 end) pledged,
MAX(CASE WHEN hoc_event.school_id is not null then 1 end) hoc_event
FROM analysis.school_stats ss
LEFT JOIN dashboard_production.schools sc on sc.id = ss.school_id
LEFT JOIN dashboard_production.school_infos si
Expand All @@ -185,6 +199,8 @@ union all
ON csf_pd.user_id = u.id
LEFT JOIN pledged
ON pledged.school_id = ss.school_id
LEFT JOIN hoc_event
ON hoc_event.school_id = ss.school_id
WHERE ss.state not in (select state from dashboard_production_pii.pd_regional_partner_mappings where state is not null)
AND ss.zip not in (select zip_code from dashboard_production_pii.pd_regional_partner_mappings where zip_code is not null)
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14;
Expand Down
14 changes: 14 additions & 0 deletions aws/redshift/tables/school_years.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
create table analysis.school_years (
school_year varchar,
started_at date,
ended_at date
);

insert into analysis.school_years values
('2015-16', '2015-07-01', '2016-06-30'),
('2016-17', '2016-07-01', '2017-06-30'),
('2017-18', '2017-07-01', '2018-06-30');

GRANT ALL PRIVILEGES ON analysis.school_years TO GROUP admin;
GRANT SELECT ON analysis.school_years TO GROUP reader, GROUP reader_pii;

36 changes: 21 additions & 15 deletions aws/redshift/views/census_details.sql
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
drop view analysis.census_details;
create view analysis.census_details as
with csa as
(
select ss.school_id, case when cso.school_code is not null then 1 else 0 end as teaches_csa
from school_stats ss
left join ap_school_codes sc on sc.school_id = ss.school_id
left join ap_cs_offerings cso on cso.school_code = sc.school_code and cso.course = 'CSA'
where ss.school_type in ('public','charter') -- only include known status for public and charter schools, as we don't have data for private schools
select ss.school_id,
case when cso.school_code is not null then 1
when cso.school_code is null and ss.school_type in ('public', 'charter') then 0 -- only include known status for public and charter schools, as we don't have data for private schools
end as teaches_csa
from analysis.school_stats ss
left join dashboard_production.ap_school_codes sc on sc.school_id = ss.school_id
left join dashboard_production.ap_cs_offerings cso on cso.school_code = sc.school_code and cso.course = 'CSA'
),
csp as
(
select ss.school_id, case when cso.school_code is not null then 1 else 0 end as teaches_csp
from school_stats ss
left join ap_school_codes sc on sc.school_id = ss.school_id
left join ap_cs_offerings cso on cso.school_code = sc.school_code and cso.course = 'CSP'
where ss.school_type in ('public','charter') -- only include known status for public and charter schools, as we don't have data for private schools
select ss.school_id,
case when cso.school_code is not null then 1
when cso.school_code is null and ss.school_type in ('public', 'charter') then 0 -- only include known status for public and charter schools, as we don't have data for private schools
end as teaches_csp
from analysis.school_stats ss
left join dashboard_production.ap_school_codes sc on sc.school_id = ss.school_id
left join dashboard_production.ap_cs_offerings cso on cso.school_code = sc.school_code and cso.course = 'CSP'
),
census as
(
select school_id,
max(case when how_many_do_hoc is null then null when how_many_do_hoc = 'I DON\'T KNOW' then null when how_many_do_hoc in ('SOME', 'ALL') then 1 else 0 end) hoc,
max(case when how_many_after_school is null then null when how_many_after_school = 'I DON\'T KNOW' then null when how_many_after_school in ('SOME', 'ALL') then 1 else 0 end) after_school
from census_submissions cs
join census_submissions_school_infos cssi on cssi.census_submission_id = cs.id
join school_infos si on si.id = cssi.school_info_id
from dashboard_production_pii.census_submissions cs
join dashboard_production.census_submissions_school_infos cssi on cssi.census_submission_id = cs.id
join dashboard_production.school_infos si on si.id = cssi.school_info_id
where school_id is not null
group by 1
)
select ss.school_id, teaches_csa, teaches_csp, hoc, after_school
from school_stats ss
from analysis.school_stats ss
left join csp on csp.school_id = ss.school_id
left join csa on csa.school_id = ss.school_id
left join census cen on cen.school_id = ss.school_id;
left join census cen on cen.school_id = ss.school_id
with no schema binding;

GRANT ALL PRIVILEGES ON analysis.census_details TO GROUP admin;
GRANT SELECT ON analysis.census_details TO GROUP reader, GROUP reader_pii;
4 changes: 2 additions & 2 deletions aws/redshift/views/course_structure.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
drop view analysis.course_structure;
create view analysis.course_structure as
select c.id course_id, c.name course_name, sl.script_id, sc.name script_name, st.id stage_id, st.name stage_name, st.absolute_position stage_number, lsl.level_id, le.name level_name, sl.position as level_number
from dashboard_production.levels_script_levels lsl
Expand All @@ -8,7 +7,8 @@ join dashboard_production.levels le on le.id = lsl.level_id
join dashboard_production.scripts sc on sc.id = sl.script_id
left join dashboard_production.course_scripts cs on cs.script_id = sc.id
left join dashboard_production.courses c on c.id = cs.course_id
order by script_id, stage_number, level_number;
order by script_id, stage_number, level_number
with no schema binding;

GRANT ALL PRIVILEGES ON analysis.course_structure TO GROUP admin;
GRANT SELECT ON analysis.course_structure TO GROUP reader, GROUP reader_pii;
20 changes: 20 additions & 0 deletions aws/redshift/views/csf_completed.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
drop view analysis.csf_completed;
create view analysis.csf_completed as
select us.user_id,
us.script_id,
us.script_name,
us.school_year,
started_at as completed_at -- starting the Nth stage (dependent on script) representing "completing" the course
from
(
select us.user_id, sc.name script_name, script_id, stage_id, school_year, us.started_at::date,
row_number() over(partition by us.user_id, us.script_id order by us.started_at asc) stage_order
from analysis.user_stages us
join dashboard_production.scripts sc on sc.id = us.script_id
join analysis.school_years sy on us.started_at between sy.started_at and sy.ended_at
) us
join analysis.csf_plugged_stage_counts sc on sc.script_id = us.script_id and us.stage_order = sc.plugged_stage_counts
with no schema binding;

GRANT ALL PRIVILEGES ON analysis.csf_completed TO GROUP admin;
GRANT SELECT ON analysis.csf_completed TO GROUP reader, GROUP reader_pii;

0 comments on commit f6bad97

Please sign in to comment.