Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/make profile public #1051

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions helpers/test/test_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

from django.test import SimpleTestCase
from rest_framework.test import APITestCase
from django.urls import resolve


class UrlTestCase(SimpleTestCase):
class UrlTestCase(APITestCase):
def assertUrlResolvesToView(self, url, view_name, kwargs=None):
resolved = resolve(url)
self.assertEqual(resolved.view_name, view_name)
Expand Down
4 changes: 4 additions & 0 deletions helpers/test/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ def get_auth_response(request, user, url, *args, **kwargs):
request.subdomain = "uoft"
view = resolve(url).func
return view(request, *args, **kwargs)


def get_profile_route(studentId):
return f"/user/{studentId}/profile/"
2 changes: 1 addition & 1 deletion parsing/schools/neu/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ searchneu.default
}

global.console.log("saved semesterly data");
}
},
);
})
.catch((err) => {
Expand Down
4 changes: 2 additions & 2 deletions static/js/redux/__tests__/modals/course_modal_body.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ it("CourseModalBody correctly renders", () => {
<CourseModalBody course={course} hideModal={() => null} />,
{
preloadedState: initialState,
}
},
);

expect(container).toHaveTextContent(
"The use of microeconomics to analyze a variety of issues from marketing and finance to organizational structure. Consumer preferences and behavior; demand, cost analysis and estimation; allocation of inputs, pricing and firm behavior under perfect and imperfect competition; game theory and public policy, including competition policy. Business cases are used to connect theory and practice and to highlight differences and similarities between economics and accounting, marketing and finance. This course is restricted to students in the Commerce programs."
"The use of microeconomics to analyze a variety of issues from marketing and finance to organizational structure. Consumer preferences and behavior; demand, cost analysis and estimation; allocation of inputs, pricing and firm behavior under perfect and imperfect competition; game theory and public policy, including competition policy. Business cases are used to connect theory and practice and to highlight differences and similarities between economics and accounting, marketing and finance. This course is restricted to students in the Commerce programs.",
);
expect(container).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe("TOS Modal", () => {
preloadedState: initialState,
});
expect(container).toHaveTextContent(
"Our Terms of Service and Privacy Policy have been updated"
"Our Terms of Service and Privacy Policy have been updated",
);
});

Expand Down
6 changes: 3 additions & 3 deletions static/js/redux/__tests__/search_actions.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("maybeSetSemester", () => {
semester: sampleSemesters,
entities,
},
applyMiddleware(thunkMiddleware)
applyMiddleware(thunkMiddleware),
);

const newSemester = 1;
Expand All @@ -51,7 +51,7 @@ describe("maybeSetSemester", () => {
const store = createStore(
combineReducers(reducers),
{ semester: sampleSemesters },
applyMiddleware(thunkMiddleware)
applyMiddleware(thunkMiddleware),
);
store.dispatch(maybeSetSemester(1));
expect(store.getState().semester.current).toEqual(1);
Expand All @@ -61,7 +61,7 @@ describe("maybeSetSemester", () => {
const store = createStore(
combineReducers(reducers),
{ semester: sampleSemesters, timetables: withTimetables, entities },
applyMiddleware(thunkMiddleware)
applyMiddleware(thunkMiddleware),
);
store.dispatch(maybeSetSemester(1));
const newState = store.getState();
Expand Down
4 changes: 2 additions & 2 deletions static/js/redux/actions/dragSearchActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const addSearchSlot =
time_start: timeStart,
time_end: timeEnd,
day,
})
}),
);
};

Expand All @@ -27,7 +27,7 @@ export const updateSearchSlot =
updateDragSearchSlot({
time_start: timeStart,
time_end: timeEnd,
})
}),
);
};

Expand Down
8 changes: 4 additions & 4 deletions static/js/redux/actions/initActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const receiveCourses = createAction("global/receiveCourses", (courses) =>
export const changeActiveTimetable = createAction("global/changeActiveTimetable");

export const changeActiveSavedTimetable = createAction(
"global/changeActiveSavedTimetable"
"global/changeActiveSavedTimetable",
);

export const receiveSchoolInfo = createAction("global/receiveSchoolInfo");
Expand All @@ -49,14 +49,14 @@ export const receiveSearchResults = createAction(
"global/receiveSearchResults",
(response) => ({
payload: { courses: normalize(response.data, [courseSchema]), page: response.page },
})
}),
);

export const receiveAdvancedSearchResults = createAction(
"global/receiveAdvancedSearchResults",
(response) => ({
payload: { courses: normalize(response.data, [courseSchema]), page: response.page },
})
}),
);

export const requestCourses = createAction("global/requestCourses");
Expand All @@ -79,5 +79,5 @@ export const setTheme = createAction("global/setTheme", (theme: Theme) => ({
*/
export const setShowWeekend = createAction(
"global/setShowWeekend",
(showWeekend: boolean) => ({ payload: showWeekend })
(showWeekend: boolean) => ({ payload: showWeekend }),
);
2 changes: 1 addition & 1 deletion static/js/redux/actions/modal_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const react = (cid, title) => (dispatch) => {
setCourseReactions({
id: cid,
reactions: json.reactions,
})
}),
);
}
});
Expand Down
30 changes: 15 additions & 15 deletions static/js/redux/actions/timetable_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ export const lockTimetable = (timetable) => (dispatch, getState) => {
}
dispatch(
courseSectionsActions.receiveCourseSections(
lockActiveSections(getDenormTimetable(state, timetable))
)
lockActiveSections(getDenormTimetable(state, timetable)),
),
);
dispatch(receiveTimetables([timetable]));
if (state.userInfo.data.isLoggedIn) {
Expand Down Expand Up @@ -187,15 +187,15 @@ export const loadTimetable =
changeActiveSavedTimetable({
timetable: displayTimetable,
upToDate: !isLoadingNewTimetable,
})
}),
);
return dispatch(lockTimetable(displayTimetable));
}
return dispatch(
changeActiveSavedTimetable({
timetable: displayTimetable,
upToDate: !isLoadingNewTimetable,
})
}),
);
};

Expand All @@ -211,8 +211,8 @@ export const createNewTimetable =
has_conflict: false,
show_weekend: false,
},
true
)
true,
),
);
};

Expand All @@ -223,7 +223,7 @@ export const nullifyTimetable = () => (dispatch) => {
changeActiveSavedTimetable({
timetable: emptyTimetable,
upToDate: false,
})
}),
);
dispatch(customEventsActions.clearCustomEvents());
};
Expand All @@ -236,22 +236,22 @@ const getSemesterIndex = function getSemesterIndex(allSemesters, oldSemesters) {
if (cachedSemesterIndex === "S") {
// last timetable was cached using old old format
cachedSemesterIndex = allSemesters.findIndex(
(s) => (s.name === "Spring" || s.name === "Winter") && s.year === "2017"
(s) => (s.name === "Spring" || s.name === "Winter") && s.year === "2017",
);
} else if (cachedSemesterIndex === "F") {
cachedSemesterIndex = allSemesters.findIndex(
(s) => s.name === "Fall" && s.year === "2016"
(s) => s.name === "Fall" && s.year === "2016",
);
}
const semester = oldSemesters[Number(cachedSemesterIndex)];
return allSemesters.findIndex(
(s) => s.name === semester.name && s.year === semester.year
(s) => s.name === semester.name && s.year === semester.year,
);
}
const cachedSemesterName = localStorage.getItem("semesterName");
const cachedYear = localStorage.getItem("year");
return allSemesters.findIndex(
(sem) => sem.name === cachedSemesterName && sem.year === cachedYear
(sem) => sem.name === cachedSemesterName && sem.year === cachedYear,
);
};

Expand Down Expand Up @@ -324,8 +324,8 @@ export const handleCreateNewTimetable = () => (dispatch, getState) => {

return dispatch(
createNewTimetable(
getNumberedName("Untitled Schedule", state.userInfo.data.timetables)
)
getNumberedName("Untitled Schedule", state.userInfo.data.timetables),
),
);
};

Expand Down Expand Up @@ -399,7 +399,7 @@ export const addCustomSlot =
credits: 0.0,
id,
preview,
})
}),
);
dispatch(savingTimetableActions.setUpToDate(false));
};
Expand Down Expand Up @@ -508,7 +508,7 @@ export const finalizeCustomSlot = (id) => (dispatch, getState) => {
customEventsActions.replacePreviewEvent({
oldId: id,
newId: newEvent.id,
})
}),
);
dispatch(savingTimetableActions.setUpToDate(true));
});
Expand Down
8 changes: 4 additions & 4 deletions static/js/redux/actions/user_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
getFriendsEndpoint,
getLoadSavedTimetablesEndpoint,
getMostClassmatesCountEndpoint,
getSaveSettingsEndpoint,
getProfileEndpoint,
getSaveTimetableEndpoint,
acceptTOSEndpoint,
} from "../constants/endpoints";
Expand Down Expand Up @@ -96,7 +96,7 @@ export const fetchMostClassmatesCount = (timetable) => (dispatch, getState) => {
mostFriendsCount: json.count,
mostFriendsClassId: json.id,
totalFriendsCount: json.total_count,
})
}),
);
});
};
Expand Down Expand Up @@ -228,7 +228,7 @@ export const saveSettings = (callback) => async (dispatch, getState) => {
dispatch(userInfoActions.requestSaveUserInfo());
// TODO: refactor all fetch promise logic to async/await axios
try {
await fetch(getSaveSettingsEndpoint(), {
await fetch(getProfileEndpoint(), {
headers: {
"X-CSRFToken": Cookie.get("csrftoken"),
Accept: "application/json",
Expand Down Expand Up @@ -305,7 +305,7 @@ export const autoSave = () => (dispatch, getState) => {
};

export const deleteUser = () => (dispatch) => {
fetch(getSaveSettingsEndpoint(), {
fetch(getProfileEndpoint(), {
headers: {
"X-CSRFToken": Cookie.get("csrftoken"),
Accept: "application/json",
Expand Down
2 changes: 1 addition & 1 deletion static/js/redux/constants/endpoints.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const getDeleteTimetableEndpoint = (semester, name) =>
`/user/timetables/${semester.name}/${semester.year}/${name}/`;
export const getTimetablePreferencesEndpoint = (id) =>
`/user/timetables/${id}/preferences/`;
export const getSaveSettingsEndpoint = () => "/user/settings/";
export const getProfileEndpoint = (userId) => `/user/${userId}/profile/`;
export const getClassmatesEndpoint = (semester, courses) =>
`/user/classmates/${semester.name}/${semester.year}?${$.param({
course_ids: courses,
Expand Down
5 changes: 3 additions & 2 deletions static/js/redux/ui/social_profile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import React from "react";
import classNames from "classnames";
import ClickOutHandler from "react-onclickout";
import * as SemesterlyPropTypes from "../constants/semesterlyPropTypes";
import { getProfileEndpoint } from "../constants/endpoints";

/**
* This component displays the student's profile picture in the top right when they are
Expand Down Expand Up @@ -62,9 +63,9 @@ class SocialProfile extends React.Component {
<i className="fa fa-cog" />
<span>Account</span>
</a>
<a href="/user/settings/">
<a href={getProfileEndpoint(this.props.userInfo.id)}>
<i className="fa fa-bar-chart" />
<span>Stats</span>
<span>Profile</span>
</a>
<a href="/user/logout/">
<i className="fa fa-sign-out" aria-hidden="true" />
Expand Down
1 change: 1 addition & 0 deletions student/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = (
"id",
"preferred_name",
"class_year",
"img_url",
Expand Down
29 changes: 21 additions & 8 deletions student/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
create_student,
get_response,
get_auth_response,
get_profile_route,
)
from helpers.test.test_cases import UrlTestCase
from timetable.serializers import EventSerializer
Expand Down Expand Up @@ -120,10 +121,16 @@ def test_accept_tos(self):
class UrlsTest(UrlTestCase):
"""Test student/urls.py"""

def setUp(self):
self.user = create_user(username="Bob", password="security")
self.student = create_student(user=self.user)

def test_urls_call_correct_views(self):
# profile management

self.assertUrlResolvesToView("/user/settings/", "student.views.UserView")
self.assertUrlResolvesToView(
get_profile_route(self.student.id), "student.views.UserView"
)

# timetable management
self.assertUrlResolvesToView(
Expand Down Expand Up @@ -173,18 +180,18 @@ def setUp(self):

def test_profile_page(self):
self.client.force_login(self.user)
response = self.client.get("/user/settings/")
response = self.client.get(get_profile_route(self.student.id))
self.assertTemplateUsed(response, "profile.html")

def test_profile_page_context(self):
self.client.force_login(self.user)
response = self.client.get("/user/settings/")
response = self.client.get(get_profile_route(self.student.id))
self.assertEquals(response.context["major"], "STAD")
self.assertEquals(response.context["student"], self.student)

def test_profile_page_not_signed_in(self):
self.client.logout()
response = self.client.get("/user/settings/")
response = self.client.get(get_profile_route(self.student.id))
self.assertRedirects(response, "/signup/")

def test_add_reactions(self):
Expand All @@ -201,8 +208,12 @@ def test_add_reactions(self):

def test_update_settings(self):
new_settings = {"emails_enabled": True, "social_courses": True, "major": "CS"}
request = self.factory.patch("/user/settings/", new_settings, format="json")
response = get_auth_response(request, self.user, "/user/settings/")
request = self.factory.patch(
get_profile_route(self.student.id), new_settings, format="json"
)
response = get_auth_response(
request, self.user, get_profile_route(self.student.id)
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.student = Student.objects.get(user=self.user)
self.assertDictContainsSubset(new_settings, model_to_dict(self.student))
Expand All @@ -228,8 +239,10 @@ def test_delete_user(self):
tt.sections.add(section)
tt.save()

request = self.factory.delete("/user/settings/")
response = get_auth_response(request, self.user, "/user/settings/")
request = self.factory.delete(get_profile_route(self.student.id))
response = get_auth_response(
request, self.user, get_profile_route(self.student.id)
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

# all student related data should be deleted
Expand Down
Loading
Loading