package com.instructure.canvasapi.api;
import android.content.Context;
import com.instructure.canvasapi.model.Attachment;
import com.instructure.canvasapi.model.Course;
import com.instructure.canvasapi.model.Enrollment;
import com.instructure.canvasapi.model.Favorite;
import com.instructure.canvasapi.model.FileUploadParams;
import com.instructure.canvasapi.model.GradingPeriodResponse;
import com.instructure.canvasapi.utilities.APIHelpers;
import com.instructure.canvasapi.utilities.CanvasCallback;
import com.instructure.canvasapi.utilities.CanvasRestAdapter;
import com.instructure.canvasapi.utilities.ExhaustiveBridgeCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import retrofit.RestAdapter;
import retrofit.http.Body;
import retrofit.http.DELETE;
import retrofit.http.GET;
import retrofit.http.Multipart;
import retrofit.http.POST;
import retrofit.http.PUT;
import retrofit.http.Part;
import retrofit.http.PartMap;
import retrofit.http.Path;
import retrofit.http.Query;
import retrofit.mime.TypedFile;
/**
* Copyright (c) 2015 Instructure. All rights reserved.
*/
public class CourseAPI extends BuildInterfaceAPI {
interface CoursesInterface {
@PUT("/courses/{courseid}")
void updateCourse(@Path("courseid") long courseID,
@Query("course[name]") String name, @Query("course[course_code]") String courseCode,
@Query("course[start_at]") String startAt, @Query("course[end_at]") String endAt,
@Query("course[license]") String license, @Query("course[is_public]") Integer isPublic,
@Body String body,
CanvasCallback<Course> callback);
@GET("/courses/{courseid}?include[]=term&include[]=permissions&include[]=license&include[]=is_public&include[]=needs_grading_count")
void getCourse(@Path("courseid") long courseId, CanvasCallback<Course> callback);
@GET("/courses/{courseid}?include[]=term&include[]=permissions&include[]=license&include[]=is_public&include[]=needs_grading_count&include[]=total_scores&include[]=current_grading_period_scores")
void getCourseWithGrade(@Path("courseid") long courseId, CanvasCallback<Course> callback);
@GET("/courses/{courseid}/enrollments")
void getEnrollmentsForGradingPeriod(@Path("courseid") long courseId, @Query("grading_period_id") long gradingPeriodId, CanvasCallback<Enrollment[]> callback);
@GET("/courses/{courseid}?include[]=syllabus_body&include[]=term&include[]=license&include[]=is_public&include[]=permissions")
void getCourseWithSyllabus(@Path("courseid") long courseId, CanvasCallback<Course> callback);
// I don't see why we wouldn't want to always get the grades
@GET("/courses?include[]=term&include[]=total_scores&include[]=license&include[]=is_public&include[]=needs_grading_count&include[]=permissions&include[]=favorites&include[]=current_grading_period_scores")
void getFirstPageCourses(CanvasCallback<Course[]> callback);
@GET("/{next}?&include[]=needs_grading_count&include[]=permissions&include[]=favorites")
void getNextPageCourses(@Path(value = "next", encode = false) String nextURL, CanvasCallback<Course[]> callback);
@GET("/users/self/favorites/courses?include[]=term&include[]=total_scores&include[]=license&include[]=is_public&include[]=needs_grading_count&include[]=permissions&include[]=current_grading_period_scores")
void getFavoriteCourses(CanvasCallback<Course[]> callback);
@GET("/courses/{courseId}/grading_periods")
void getGradingPeriodsForCourse(@Path("courseId") long courseId, CanvasCallback<GradingPeriodResponse> callback);
@POST("/users/self/favorites/courses/{courseId}")
void addCourseToFavorites(@Path("courseId") long courseId, @Body String body, CanvasCallback<Favorite> callback);
@DELETE("/users/self/favorites/courses/{courseId}")
void removeCourseFromFavorites(@Path("courseId") long courseId, CanvasCallback<Favorite> callback);
@GET("/users/{user_id}/courses?include[]=total_scores&include[]=syllabus_body")
void getCoursesForUser(@Path("user_id") long userId, CanvasCallback<Course[]> callback);
@GET("/{next}")
void getNextPageCoursesForUser(@Path(value = "next", encode = false) String nextURL, CanvasCallback<Course[]> callback);
@GET("/canvas/{parentId}/{studentId}/courses?include[]=total_scores&include[]=syllabus_body&include[]=current_grading_period_scores")
void getCoursesForUserAirwolf(@Path("parentId") String parentId, @Path("studentId") String studentId, CanvasCallback<Course[]> callback);
@GET("/canvas/{parentId}/{studentId}/courses/{courseId}?include[]=syllabus_body&include[]=term&include[]=license&include[]=is_public&include[]=permissions")
void getCourseWithSyllabusAirwolf(@Path("parentId") String parentId, @Path("studentId") String studentId, @Path("courseId") long courseId, CanvasCallback<Course> callback);
@GET("/canvas/{parentId}/{studentId}/courses/{courseId}?include[]=term&include[]=permissions&include[]=license&include[]=is_public&include[]=needs_grading_count&include[]=total_scores&include[]=current_grading_period_scores")
void getCourseWithGradeAirwolf(@Path("parentId") String parentId, @Path("studentId") String studentId, @Path("courseId") long courseId, CanvasCallback<Course> callback);
/////////////////////////////////////////////////////////////////////////////
// Synchronous
/////////////////////////////////////////////////////////////////////////////
@GET("/courses?include[]=term&include[]=total_scores&include[]=license&include[]=is_public&include[]=permissions")
Course[] getAllCoursesSynchronous(@Query("page") int page);
@GET("/users/self/favorites/courses?include[]=term&include[]=total_scores&include[]=license&include[]=is_public&include[]=permissions")
Course[] getFavCoursesSynchronous(@Query("page") int page);
@POST("/courses/{courseId}/files")
FileUploadParams getFileUploadParams(@Path("courseId") long courseId, @Query("parent_folder_id") Long parentFolderId, @Query("size") long size, @Query("name") String fileName, @Query("content_type") String content_type, @Body String body);
@Multipart
@POST("/")
Attachment uploadCourseFile(@PartMap LinkedHashMap<String, String> params, @Part("file") TypedFile file);
}
/////////////////////////////////////////////////////////////////////////
// API Calls
/////////////////////////////////////////////////////////////////////////
public static void getCourse(long courseId, CanvasCallback<Course> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback, false).getCourse(courseId, callback);
buildInterface(CoursesInterface.class, callback, false).getCourse(courseId, callback);
}
public static void getCourseWithGrade(long courseId, CanvasCallback<Course> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback, false).getCourseWithGrade(courseId, callback);
buildInterface(CoursesInterface.class, callback, false).getCourseWithGrade(courseId, callback);
}
public static void getCourseWithSyllabus(long courseId, CanvasCallback<Course> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback, false).getCourseWithSyllabus(courseId, callback);
buildInterface(CoursesInterface.class, callback, false).getCourseWithSyllabus(courseId, callback);
}
public static void getFirstPageCourses(CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback).getFirstPageCourses(callback);
buildInterface(CoursesInterface.class, callback).getFirstPageCourses(callback);
}
public static void getFirstPageFavoriteCourses(CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback).getFavoriteCourses(callback);
buildInterface(CoursesInterface.class, callback).getFavoriteCourses(callback);
}
public static void getGradingPeriodsForCourse(long courseId, CanvasCallback<GradingPeriodResponse> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback).getGradingPeriodsForCourse(courseId, callback);
buildInterface(CoursesInterface.class, callback).getGradingPeriodsForCourse(courseId, callback);
}
public static void getEnrollmentsForGradingPeriod(long courseId, long gradingPeriodId, CanvasCallback<Enrollment[]> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildCacheInterface(CoursesInterface.class, callback).getEnrollmentsForGradingPeriod(courseId, gradingPeriodId, callback);
buildInterface(CoursesInterface.class, callback).getEnrollmentsForGradingPeriod(courseId, gradingPeriodId, callback);
}
public static void getNextPageCourses(CanvasCallback<Course[]> callback, String nextURL) {
if (APIHelpers.paramIsNull(callback, nextURL)) return;
callback.setIsNextPage(true);
buildCacheInterface(CoursesInterface.class, callback).getNextPageCourses(nextURL, callback);
buildInterface(CoursesInterface.class, callback).getNextPageCourses(nextURL, callback);
}
public static void getNextPageCoursesChained(CanvasCallback<Course[]> callback, String nextURL, boolean isCached) {
if (APIHelpers.paramIsNull(callback, nextURL)) return;
callback.setIsNextPage(true);
if (isCached) {
buildCacheInterface(CoursesInterface.class, callback).getNextPageCourses(nextURL, callback);
} else {
buildInterface(CoursesInterface.class, callback).getNextPageCourses(nextURL, callback);
}
}
public static void addCourseToFavorites(final long courseId, final CanvasCallback<Favorite> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildInterface(CoursesInterface.class, callback).addCourseToFavorites(courseId, "", callback);
}
public static void removeCourseFromFavorites(final long courseId, final CanvasCallback<Favorite> callback) {
if (APIHelpers.paramIsNull(callback)) return;
buildInterface(CoursesInterface.class, callback).removeCourseFromFavorites(courseId, callback);
}
public static void getAllFavoriteCoursesChained(final CanvasCallback<Course[]> callback, boolean isCached) {
if (APIHelpers.paramIsNull(callback)) return;
CanvasCallback<Course[]> bridge = new ExhaustiveBridgeCallback<>(Course.class, callback, new ExhaustiveBridgeCallback.ExhaustiveBridgeEvents() {
@Override
public void performApiCallWithExhaustiveCallback(CanvasCallback bridgeCallback, String nextURL, boolean isCached) {
if(callback.isCancelled()) { return; }
CourseAPI.getNextPageCoursesChained(bridgeCallback, nextURL, isCached);
}
});
if (isCached) {
buildCacheInterface(CoursesInterface.class, callback).getFavoriteCourses(bridge);
} else {
buildInterface(CoursesInterface.class, callback).getFavoriteCourses(bridge);
}
}
public static void getAllFavoriteCourses(final CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(callback)) return;
CanvasCallback<Course[]> bridge = new ExhaustiveBridgeCallback<>(Course.class, callback, new ExhaustiveBridgeCallback.ExhaustiveBridgeEvents() {
@Override
public void performApiCallWithExhaustiveCallback(CanvasCallback bridgeCallback, String nextURL, boolean isCached) {
if(callback.isCancelled()) { return; }
CourseAPI.getNextPageCoursesChained(bridgeCallback, nextURL, isCached);
}
});
buildCacheInterface(CoursesInterface.class, callback).getFavoriteCourses(bridge);
buildInterface(CoursesInterface.class, callback).getFavoriteCourses(bridge);
}
public static void getAllCourses(final CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(callback)) return;
CanvasCallback<Course[]> bridge = new ExhaustiveBridgeCallback<>(Course.class, callback, new ExhaustiveBridgeCallback.ExhaustiveBridgeEvents() {
@Override
public void performApiCallWithExhaustiveCallback(CanvasCallback bridgeCallback, String nextURL, boolean isCached) {
if(callback.isCancelled()) { return; }
CourseAPI.getNextPageCoursesChained(bridgeCallback, nextURL, isCached);
}
});
buildCacheInterface(CoursesInterface.class, callback).getFirstPageCourses(bridge);
buildInterface(CoursesInterface.class, callback).getFirstPageCourses(bridge);
}
/**
* @param newCourseName (Optional)
* @param newCourseCode (Optional)
* @param newStartAt (Optional)
* @param newEndAt (Optional)
* @param license (Optional)
* @param newIsPublic (Optional)
* @param course (Required)
* @param callback (Required)
*/
public static void updateCourse(String newCourseName, String newCourseCode, Date newStartAt, Date newEndAt, Course.LICENSE license, Boolean newIsPublic, Course course, CanvasCallback<Course> callback) {
if (APIHelpers.paramIsNull(callback, course)) return;
String newStartAtString = APIHelpers.dateToString(newStartAt);
String newEndAtString = APIHelpers.dateToString(newEndAt);
Integer newIsPublicInteger = (newIsPublic == null) ? null : APIHelpers.booleanToInt(newIsPublic);
buildInterface(CoursesInterface.class, callback).updateCourse(course.getId(), newCourseName, newCourseCode, newStartAtString, newEndAtString, Course.licenseToAPIString(license), newIsPublicInteger, "", callback);
}
public static void getCoursesForUser(long userId, CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(callback)) { return; }
buildCacheInterface(CoursesInterface.class, callback).getCoursesForUser(userId, callback);
buildInterface(CoursesInterface.class, callback).getCoursesForUser(userId, callback);
}
public static void getNextPageCoursesForUser(String nextURL, CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(nextURL, callback)) { return; }
callback.setIsNextPage(true);
buildCacheInterface(CoursesInterface.class, callback).getNextPageCoursesForUser(nextURL, callback);
buildInterface(CoursesInterface.class, callback).getNextPageCoursesForUser(nextURL, callback);
}
public static void getCoursesForUserAirwolf(String parentId, String studentId, CanvasCallback<Course[]> callback) {
if(APIHelpers.paramIsNull(parentId, studentId, callback)) { return; }
buildCacheInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback).getCoursesForUserAirwolf(parentId, studentId, callback);
buildInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback).getCoursesForUserAirwolf(parentId, studentId, callback);
}
public static void getNextPageCoursesForUserAirwolf(String nextURL, CanvasCallback<Course[]> callback) {
if (APIHelpers.paramIsNull(nextURL, callback)) { return; }
callback.setIsNextPage(true);
buildCacheInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback).getNextPageCoursesForUser(nextURL, callback);
buildInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback).getNextPageCoursesForUser(nextURL, callback);
}
public static void getCourseWithSyllabusAirwolf(String parentId, String studentId, long courseId, CanvasCallback<Course> callback) {
if (APIHelpers.paramIsNull(parentId, studentId, callback)) return;
buildCacheInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback, false).getCourseWithSyllabusAirwolf(parentId, studentId, courseId, callback);
buildInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback, false).getCourseWithSyllabusAirwolf(parentId, studentId, courseId, callback);
}
public static void getCourseWithGradeAirwolf(String parentId, String studentId, long courseId, CanvasCallback<Course> callback) {
if (APIHelpers.paramIsNull(parentId, studentId, callback)) return;
buildCacheInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback, false).getCourseWithGradeAirwolf(parentId, studentId, courseId, callback);
buildInterface(CoursesInterface.class, APIHelpers.getAirwolfDomain(callback.getContext()), callback, false).getCourseWithGradeAirwolf(parentId, studentId, courseId, callback);
}
/////////////////////////////////////////////////////////////////////////////
// Helper Methods
////////////////////////////////////////////////////////////////////////////
public static Map<Long, Course> createCourseMap(Course[] courses) {
Map<Long, Course> courseMap = new HashMap<Long, Course>();
if(courses == null) {
return courseMap;
}
for (Course course : courses) {
courseMap.put(course.getId(), course);
}
return courseMap;
}
/////////////////////////////////////////////////////////////////////////////
// Synchronous
//
// If Retrofit is unable to parse (no network for example) Synchronous calls
// will throw a nullPointer exception. All synchronous calls need to be in a
// try catch block.
/////////////////////////////////////////////////////////////////////////////
public static Course[] getAllCoursesSynchronous(Context context) {
RestAdapter restAdapter = CanvasRestAdapter.buildAdapter(context);
//If not able to parse (no network for example), this will crash. Handle that case.
try {
ArrayList<Course> allCourses = new ArrayList<>();
int page = 1;
long firstItemId = -1;
//for(ever) loop. break once we've run outta stuff;
for (;;) {
Course[] courses = restAdapter.create(CoursesInterface.class).getAllCoursesSynchronous(page);
page++;
//This is all or nothing. We don't want partial data.
if(courses == null){
return null;
} else if (courses.length == 0) {
break;
} else if(courses[0].getId() == firstItemId){
break;
} else {
firstItemId = courses[0].getId();
Collections.addAll(allCourses, courses);
}
}
return allCourses.toArray(new Course[allCourses.size()]);
} catch (Exception E) {
return null;
}
}
public static Course[] getFavCoursesSynchronous(Context context) {
RestAdapter restAdapter = CanvasRestAdapter.buildAdapter(context);
//If not able to parse (no network for example), this will crash. Handle that case.
try {
ArrayList<Course> allCourses = new ArrayList<>();
int page = 1;
long firstItemId = -1;
//for(ever) loop. break once we've run outta stuff;
for (;;) {
Course[] courses = restAdapter.create(CoursesInterface.class).getFavCoursesSynchronous(page);
page++;
//This is all or nothing. We don't want partial data.
if(courses == null){
return null;
} else if (courses.length == 0) {
break;
} else if(courses[0].getId() == firstItemId){
break;
} else {
firstItemId = courses[0].getId();
Collections.addAll(allCourses, courses);
}
}
return allCourses.toArray(new Course[allCourses.size()]);
} catch (Exception E) {
return null;
}
}
public static FileUploadParams getFileUploadParams(Context context, long courseId, Long parentFolderId, String fileName, long size, String contentType){
return buildInterface(CoursesInterface.class, context).getFileUploadParams(courseId, parentFolderId, size, fileName, contentType, "");
}
public static Attachment uploadCourseFile(Context context, String uploadUrl, LinkedHashMap<String,String> uploadParams, String mimeType, File file){
return buildUploadInterface(CoursesInterface.class, uploadUrl).uploadCourseFile(uploadParams, new TypedFile(mimeType, file));
}
}