NOTICE: This API is no longer being updated, and as of the 2017-2018 school year has some problems with the most recent version of Focus. Future development of this API is now part of the FocusSIS android app project.
A simple RESTful Flask server to retrieve and parse pages from the Focus for Schools Student Information System (SIS). Although it was created for the Academy for Science and Design, this service should work for any school running the Focus SIS (see supporting additional schools for details).
Complete
- Authentication
- Setting semester/year
- Portal
- Courses
- Calendar (monthly)
- Detailed information about events from calendar
- Demographic
- Schedule
- Referrals
- Absences
- Address information
- Term exam grades
- Overall term grades
Unplanned
- Attendance chart
- Preferences
Before doing anything else, make sure you have all of the dependencies installed:
pip3 install flask flask-compress requests beautifulsoup4 python-dateutil parse
Next, clone the repository and run the app.py
to start the server. In it's default configuration, it will host itself locally on your machine at http://127.0.0.1:5000/api/v3.
git clone https://github.com/kidcontact/focus-api.git && cd focus-api
python3 focus/app.py
By default, this module will attempt to connect to ASDNH's Focus servers. In order to modify the program to work for other schools, constants need be changed at the top of app.py
.
tld = 'https://focus.asdnh.org/'
urls = {
'login': tld + 'focus/index.php',
'portal': tld + 'focus/Modules.php?modname=misc/Portal.php',
'course_pre': tld + 'focus/Modules.php?modname=Grades/StudentGBGrades.php?course_period_id=',
'schedule': tld + 'focus/Modules.php?modname=Scheduling/Schedule.php',
'calendar_pre': tld + 'focus/Modules.php?modname=School_Setup/Calendar.php&',
'demographic': tld + 'focus/Modules.php?modname=Students/Student.php',
'absences': tld + 'focus/Modules.php?modname=Attendance/StudentSummary.php',
}
As you can see, all URLs are built dynamically from a given top level domain. It may be enough to simply change the TLD, but if your implementation of Focus differs slightly, the URLs in urls
may also need to be modified. Additionally, the session timeout in focus/session.py
may need to be changed to match your school's settings. If there are other bugs related to your specific school, please open an issue here and I will look into it as soon as possible.
The API does its best to follow RESTful guidelines while still connecting as an unprivileged user to Focus servers. As such, using the API should be fairly self explanatory. All payloads are passed and returned using JSON unless otherwise specified.
# Python example using requests
# log in and retrieve session cookie
d = {'username':'your.username', 'password':'yourpassword'}
r = requests.post('http://127.0.0.1:5000/api/v3/session', json=d)
sess_id = r.cookies
# change the marking period to semester two of 2015
d = {'year': 2015, 'mp_id': 315}
r = requests.put('http://127.0.0.1:5000/api/v3/session', json=d, cookies=sess_id)
# retrieve and print the student's courses
r = requests.get('http://127.0.0.1:5000/api/v3/portal', cookies=sess_id)
print(r.json()['courses'])
Accepts: GET, POST, PUT
Returns the username and timeout (in seconds, UTC) associated with the session id cookie provided. 403s when there is no session associated with the cookie.
{
"timeout": 1481819260.7088594,
"username": "stephan.lensky"
}
Takes JSON formatted login information and attempts to log in to Focus. Returns some information about the login and a session cookie if your login was successful. 401s if it was not. All other endpoints require a valid session ID cookie in order to work.
Sent:
{
"username": "your.username"
"password": "yourpassword"
}
Returned:
// JSON
{
"timeout": 1481819260.7088594,
"username": "your.username"
}
// Cookie
{
PHPSESSID: "your_session_id"
}
Updates the marking period given a year and marking period id. An additional redirect
parameter may be given to specify the page returned after changing the marking period. Valid redirect values follow the same format as API urls. So for example, to retrieve the new portal, send portal
. To retrieve course 15206, send course/15206
. Keep in mind, not all pages can be redirected to. The list of valid redirects are as follows:
- portal
- course/<id>
- schedule
- demographic
- address
- referrals
- absences
When no redirect is specified, the portal of the new marking period will be provided.
{
"year": 2015
"mp_id": 315
"redirect": "course/15206" // optional
}
The JSON returned will be equivalent to sending a GET request to course/15206
. However, doing both the marking period change and redirection in one step takes less time.
Accepts: GET
Returns a JSON object in the following format with information from the portal page:
{
"events": [
{
"description": "Spark Conference"
"date": '2016-12-14'
},
...
],
"courses": {
"11136": {
"assignments": [
{
"due": "2017-02-14T00:00:00",
"name": "Status Check #4"
},
...
],
"days": "MWH",
"id": "11136",
"letter_grade": "A+",
"name": "Learning Studios",
"percent_grade": 100,
"period": 1,
"teacher": "Douglass Adam Belley"
},
...
},
"alert": "You have been absent 9 periods" // may not be present
// marking period information
}
Accepts: GET
Returns information about all courses that the student has. This endpoint scrapes information from every course page individually, which means that it has to load up to eight full pages in order to retrieve everything. As a result calling this method will take quite some time. Use it at your own risk.
{
"courses": {
"11136": {
// see below for course format
},
...
}
// marking period information
}
Accepts: GET
Returns information about the course ID in the URL. Make sure that you are in the same marking period as the course ran, or this will not return any information about the course. Note that not all fields are applicable for all courses and assignments. For example, some courses do not use categories or have any assignments. Some assignments may not be graded, or may be a pass/fail grade. This example has both categories and assignments for your convenience.
{
"assignments": [
{
"assigned": "2016-10-20T00:00:00",
"category": "Project Work",
"due": "2016-12-19T00:00:00",
"max_grade": 50,
"name": "Lit Review Rough Draft",
"status": "ng"
},
{
"assigned": "2016-11-30T00:00:00",
"category": "Project Work",
"due": "2016-11-30T00:00:00",
"name": "Outline and Annotated Bibliography Conference",
"status": "pass"
},
{
"assigned": "2016-11-02T00:00:00",
"category": "Project Work",
"due": "2016-11-03T00:00:00",
"letter_overall_grade": "A+",
"max_grade": 3,
"name": "Status Check 3",
"percent_overall_grade": 100,
"status": "graded",
"student_grade": 3.0
},
...
],
"categories": [
{
"letter_grade": "A+",
"name": "Project Work",
"percent_grade": 100,
"percent_weight": 80
},
...
],
"id": "11136",
"letter_grade": "A+",
"name": "Learning Studios",
"percent_grade": 100,
"period": 1,
"teacher": "Douglass Adam Belley"
// marking period information
}
Accepts: GET
Returns a student's full year schedule, taken from Focus's "Class Registration/Schedule" page. The student who's schedule was used for this example has more courses than those listed, but they have ommitted so as to not take as much space.
{
"courses": [
{
"days": "MWH",
"name": "Learning Studios",
"period": 1,
"room": "161",
"teacher": "Douglass Adam Belley",
"term": "year"
},
...
{
"days": "MTWHF",
"name": "Blue Advisory",
"room": "161",
"teacher": "Patricia Ann Sockey",
"term": "year"
},
...
{
"days": "MWH",
"name": "Economics",
"period": 7,
"room": "147",
"teacher": "Kimberly A Cashin",
"term": "s2"
},
...
],
// marking period information
}
Accepts: GET
Returns a calendar for the specificity provided. The day may be omitted to give a full month, and the month may be omitted to get a full year. Focus provides calendars only by month, so retrieving a full year's calendar is very slow. To retrieve additional information about events, use calendar/assignments/\<id>
and calendar/occasions/\<id>
, depending on the type of the event.
{
"events": {
"92121": {
"date": "2016-11-02",
"id": "92121",
"name": "Spanish American War and March of the Flag Source ORQs",
"type": "assignment"
},
"697": {
"date": "2016-11-07",
"id": "697",
"name": "Progress Reports",
"type": "occasion"
},
...
},
// if you request a calendar only for one day, that day will be listed here as well
// for a full year, only the year will be listed
"month": 11,
"year": 2016
// marking period information
}
Accepts: GET
Retrieves detailed information about a calendar event of type occasion
. If the event does not exist, a status code of 400 is returned. Please note that this is not the method for calendar events of type assignment
. Additionally, this endpoint does not return any information about current or available marking periods.
{
"date": "2016-12-14",
"id": "792",
"school": "Academy for Science and Design",
"title": "SPARK Conference ",
"type": "event"
}
Accepts: GET
Retrieves detailed information about a calendar event of type assignment
. If the assignment does not exist, a status code of 400 is returned. Please note that this is not the method for calendar events of type occasion
. Additionally, this endpoint does not return any information about current or available marking periods.
{
"course": {
"days": "TWF",
"name": "Advanced Computer Science",
"period": 4,
"teacher": "Madge Smith"
},
"date": "2015-12-11",
"id": "1145",
"notes": "Binary Calculator",
"school": "Academy for Science and Design",
"title": "Assignment 7",
"type": "assignment"
}
Accepts: GET
Returns basic information about the student account and the student's most recent recorded picture. The picture is encoded as a base64 jpg. Some information in the example has been redacted for privacy reasons.
{
"arrival_bus": [REDACTED],
"birthday": [REDACTED],
"cumulative_file": true,
"dismissal_bus": [REDACTED],
"email": [REDACTED],
"force_pass_change": false,
"gender": "male",
"grade": 11,
"id": [REDACTED],
"level": 6,
"locker": 437,
"medical_record_status": "need emergency form",
"name": "Stephan Lensky",
"pass_length": [REDACTED],
"photo_auth": true,
"picture": "/9j/4AAQSkZJRgA...7i0zUDmcD3qSH/VLQB//Z",
"username": "stephan.lensky"
// marking period information
}
Accepts: GET
Retrieves information from the address page in Focus. This includes address/contact information for the student and any contacts (usually parents) associated with the student. As with the demographic section, some information has been redacted.
{
"address": [REDACTED],
"city": "Nashua",
// some fields may not be present for all contacts, make sure to check
"contacts": [
{
"address": [REDACATED],
"cell_phone": [REDACTED],
"city": "Nashua",
"email": [REDACTED],
"home_phone": [REDACTED],
"name": [REDACTED],
"private_email": [REDACTED],
"relationship": "mother",
"state": "NH",
"zip": "03063"
},
...
],
"phone": [REDACTED],
"state": "NH",
"zip": "03063"
// marking period information
}
Accepts: GET
Returns a list of referrals that the student has receieved during the current school year. If the student has no referrals, the referrals
array is empty and only the marking period information is returned.
{
"referrals": {
"3168": {
// see below for referrals format
},
...
}
// marking period information
}
Accepts: GET
Returns information about a single referral (that was given during the selected year). If there is no referral with the id given, a status code of 404 will be returned.
{
"creation_date": "2016-10-13",
"display": true,
"entry_date": "2016-10-13",
"grade": 11,
"id": "3168",
"last_updated": "2016-10-13",
"name": "Stephan Lensky",
"notification_sent": 0,
"other_violation": "Would not stop when asked",
"processed": true,
"school": "Academy for Science and Design",
"school_year": 2016,
"teacher": "Douglass Belley",
"violation": "Eating in classroom"
// marking period information
}
Accepts: GET
Returns information from the table in the "Absences" section of Focus.
{
"absences": {
"2017-02-22": {
"date": "2017-02-22",
"periods": [
"1": {
"days": "MWH",
"last_updated": "2017-02-22T08:19:21",
"last_updated_by": "Douglass Adam Belley",
"name": "Learning Studios",
"period": 1,
"status": "absent",
"teacher": "Douglass Adam Belley"
},
"2": {
"period": 2,
"status": "unset"
},
"3": {
"days": "MWH",
"last_updated": "2017-02-22T10:17:34",
"last_updated_by": "Patricia Ann Sockey",
"name": "Humanities III (LA)",
"period": 3,
"status": "absent",
"teacher": "Patricia Ann Sockey"
},
"4": {
"period": 4,
"status": "unset"
},
"5" {
"period": 5,
"status": "unset"
},
...
"advisory": {
"period": "advisory",
"status": "absent"
}
],
"status": "present"
},
...
},
"days_absent": 12.5,
"days_absent_excused": 9,
"days_absent_percent": 7.86,
"days_absent_unexcused": 2,
"days_attended": 146.5,
"days_attended_percent": 92.14,
"days_other_marks": 22,
"days_partially_absent": 31,
"days_possible": 159.0,
"periods_absent": 126,
"periods_absent_excused": 96,
"periods_absent_unexcused": 30,
"periods_late": 10,
"periods_misc": 15,
"periods_offsite": 52,
"periods_other_marks": 77,
"periods_tardy": 0
// marking period information
}
Accepts: GET
Retrieves a list of all term exams that the student has taken. If a field is blank in Focus, it may not be present in the return for this endpoint. Additionally, this endpoint does not include marking period information.
{
"exams": {
"207044": {
// see below for exam format
},
...
}
}
Accepts: GET
Retrieves information about a single exam. As with the above, different fields may be present for different exams and marking period information will not be included.
{
"affects_gpa": true,
"course_id": "10788",
"course_num": "MA3300",
"credits": 1.0,
"credits_earned": 1.0,
"gpa_points": 3.0,
"grade_level": 10,
"id": "207044",
"last_updated": "2016-06-20",
"last_updated_by": "Rosy Gandhi",
"letter_grade": "B",
"location": "Academy for Science and Design",
"mp_id": "315",
"mp_name": "Semester 2",
"name": "Precalculus Honors",
"percent_grade": 85,
"scale": "Honors & AP",
"subject": "Math",
"syear": 2015,
"teacher": "Rosy Gandhi",
"weighted_gpa_points": 3.5
}
final_grades, final_grades/<id>, semester_grades, semester_grades/<id>, quarter_grades, quarter_grades/<id>
Accepts: GET
Gets information about overall grades for the duration of the class, by semester, and by quarter respectively. Information about specific grades can be retrieved by ID. These endpoints are all lumped together due to their very similar nature.
{
"affects_gpa": true,
"comment": "Good programmer! Likes to explore.",
"course_id": "10853",
"course_num": "CS4500",
"credits": 1.0,
"credits_earned": 1.0,
"gpa_points": 3.7,
"grade_level": 10,
"id": "205253",
"last_updated": "2016-06-20",
"last_updated_by": "Madge Smith",
"letter_grade": "A-",
"location": "Academy for Science and Design",
"mp_id": "313",
"mp_name": "Semester 2",
"name": "Advanced Computer Science",
"percent_grade": 90,
"scale": "Honors & AP",
"subject": "Areas of Specialization & Inspiration",
"syear": 2015,
"teacher": "Madge Smith",
"weighted_gpa_points": 4.2
}