diff --git a/docker/prod-values.yml b/docker/prod-values.yml
index adfeb16fc6..4100c79fd2 100644
--- a/docker/prod-values.yml
+++ b/docker/prod-values.yml
@@ -178,7 +178,7 @@ cronjob:
cpu: 25m
memory: 32Mi
- name: course-cleanup
- schedule: "1 3 * 1,4,7,9 1"
+ schedule: "21 20 * * 5"
command: ["/scripts/management_command.sh"]
args: ["db_cleanup", "course"]
resources:
@@ -189,7 +189,7 @@ cronjob:
cpu: 25m
memory: 128Mi
- name: seenreg-cleanup
- schedule: "1 3 * 1,4,7,9 2"
+ schedule: "1 23 * * 5"
command: ["/scripts/management_command.sh"]
args: ["db_cleanup", "seenreg"]
resources:
@@ -200,7 +200,7 @@ cronjob:
cpu: 25m
memory: 128Mi
- name: notice-cleanup
- schedule: "1 3 * * 3"
+ schedule: "1 21 * * 5"
command: ["/scripts/management_command.sh"]
args: ["db_cleanup", "notice"]
resources:
@@ -211,7 +211,7 @@ cronjob:
cpu: 25m
memory: 512Mi
- name: linkvisit-cleanup
- schedule: "1 3 * * 4"
+ schedule: "1 22 * * 5"
command: ["/scripts/management_command.sh"]
args: ["db_cleanup", "linkvisit"]
resources:
@@ -231,7 +231,7 @@ environmentVariables:
- name: BOOKSTORE_ENV
value: PROD
- name: BOOK_POOL_SIZE
- value: "25"
+ value: "40"
- name: BOOK_TIMEOUT
value: "15"
diff --git a/myuw/dao/textbook.py b/myuw/dao/textbook.py
index 66f4eee0b0..28b90bd4b8 100644
--- a/myuw/dao/textbook.py
+++ b/myuw/dao/textbook.py
@@ -5,7 +5,8 @@
encapsulates the interactions with the Bookstore web service.
"""
-from uw_bookstore import Bookstore
+from uw_bookstore.digital_material import IACoursesStatus as Bookstore
+from myuw.dao.pws import get_regid_of_current_user
bookstore = Bookstore()
@@ -22,3 +23,15 @@ def get_order_url_by_schedule(schedule):
returns a link to the bookstore ordering page for a given schedule
"""
return bookstore.get_url_for_schedule(schedule)
+
+
+def get_iacourse_status(request, term):
+ """
+ MUWM-5272
+ returns a TermIACourse object if has data, otherwith return None
+ """
+ terms_iacourses = bookstore.get_iacourse_status(
+ get_regid_of_current_user(request)
+ )
+ key = "{}{}".format(term.quarter, term.year)
+ return terms_iacourses.get(key)
diff --git a/myuw/data/resource_link_import.csv b/myuw/data/resource_link_import.csv
index 122651237a..2c4ddb37a1 100644
--- a/myuw/data/resource_link_import.csv
+++ b/myuw/data/resource_link_import.csv
@@ -65,7 +65,7 @@ Campus Life,Campus Retail,all,,,http://www.bookstore.washington.edu/student_facu
Campus Life,Campus Safety,all,,,https://www.washington.edu/safety/,Campus Safety,https://www.uwb.edu/safety,Campus Safety Department,https://www.tacoma.uw.edu/uwt/fa/safety/,Campus Safety & Security,no
Campus Life,Campus Safety,all,http://www.washington.edu/safecampus/,SafeCampus,,,,,,,no
Campus Life,Campus Safety,all,,,https://www.ehs.washington.edu/,Environmental Health & Safety,https://www.uwb.edu/safety/ehs-emergency/environment-health-safety,Environmental Health & Safety,https://www.tacoma.uw.edu/fa/environmental-health-safety,Environmental Health & Safety,no
-Campus Life,Campus Safety,all,https://cspc.admin.uw.edu/mychem,MyChem,,,,,,,no
+Campus Life,Campus Safety,all,https://mychem.ehs.washington.edu/,MyChem,,,,,,,no
Campus Life,Campus Safety,all,,,https://www.washington.edu/uwem/,UW Emergency Management,https://www.uwb.edu/emergency,UW Bothell Emergency Page,https://www.tacoma.uw.edu/fa/safety/emergency-preparedness,UW Tacoma Emergency Response,no
Campus Life,Campus Safety,all,https://www.washington.edu/titleix/report/,Make a Title IX report,,,,,,,no
Campus Life,Getting Around Campus,all,,,http://www.washington.edu/maps/,Campus Map,https://www.uwb.edu/wp-content/uploads/2023/09/uw-bothell-campus-map.pdf,Campus Map,http://www.tacoma.uw.edu/campus-map/campus-map,Campus Map,no
@@ -78,7 +78,7 @@ Campus Life,Health & Wellness,all,https://wellbeing.uw.edu/,Husky Health & Well-
Campus Life,Health & Wellness,all,,,http://www.washington.edu/counseling/,Counseling Center,https://www.uwb.edu/student-affairs/counseling,Counseling Services,https://www.tacoma.uw.edu/uwt/caps,Counseling & Psychological Services,no
Campus Life,Health & Wellness,all,,,https://wellbeing.uw.edu/unit/hall-health/,Hall Health Center,https://www.uwb.edu/arc/hawrc,Health and Wellness Resource Center,https://www.tacoma.uw.edu/sh,Student Health Services (SHS),no
Campus Life,Health & Wellness,all,,,https://wellbeing.uw.edu/unit/livewell/,LiveWell Office,,,,,no
-Campus Life,Health & Wellness,all,,,http://www.washington.edu/ima/,Recreational Sports Programs (IMA),http://www.uwb.edu/recwell,Recreation and Wellness,http://www.tacoma.uw.edu/uwy/,Recreation & Fitness,no
+Campus Life,Health & Wellness,all,,,http://www.washington.edu/ima/,Recreational Sports Programs (IMA),https://www.uwb.edu/arc/,Activities and Recreation Center(ARC),http://www.tacoma.uw.edu/uwy/,Recreation & Fitness,no
Campus Life,Health & Wellness,all,https://www.washington.edu/wholeu/,Whole U (for staff and faculty),,,,,,,no
Campus Life,Housing & Dining,all,,,https://www.hfs.washington.edu/olco/Secure/AccountSummary.aspx,Husky Card Account,https://www.hfs.washington.edu/olco/Secure/AccountSummary.aspx,Husky Card Account,http://apps.tacoma.uw.edu/dawgdollars/,Tacoma Dawg Dollars Account,no
Campus Life,Housing & Dining,all,,,https://www.hfs.washington.edu/dining/,Campus Dining,http://www.uwb.edu/food,Campus Dining,https://www.tacoma.uw.edu/uwt/fa/facilities/food-services,Campus Dining,no
@@ -90,7 +90,6 @@ Campus Life,Rules and Regulations,all,http://www.washington.edu/students/reg/fer
Campus Life,Rules and Regulations,all,,,https://ap.washington.edu/ormp/,Office of Research Misconduct Proceedings,https://www.uwb.edu/student-affairs/studentconduct/student-guide,Student Guide to Academic Integrity,https://ap.washington.edu/ormp/,Office of Research Misconduct Proceedings,no
Campus Life,Rules and Regulations,all,,,http://www.washington.edu/cssc/student-conduct-overview/student-code-of-conduct/,Student Conduct Code,https://www.uwb.edu/student-affairs/studentconduct,Student Conduct and Responsibility,https://www.tacoma.uw.edu/student-conduct/student-code,Student Conduct Code,no
Campus Life,Support Services,all,https://www.washington.edu/circle/,Center for International Relations & Cultural Leadership Exchange (CIRCLE),,,,,,,no
-Campus Life,Support Services,all,,,,,https://www.uwb.edu/cie,Center for International Studies,,,no
Campus Life,Support Services,all,http://www.fiuts.org/,Foundation for International Understanding Through Students (FIUTS),,,,,,,no
Campus Life,Support Services,all,https://grad.uw.edu/current-students/student-success/,Graduate School Student Success,,,,,,,no
Campus Life,Support Services,all,,,http://www.washington.edu/admin/dso/uw-resources.html,Disability Resources,https://www.uwb.edu/student-affairs/drs,Disability Resources for Students (DRS),http://www.tacoma.uw.edu/drsuwt,Disability Resources for Students,no
@@ -121,7 +120,7 @@ Events and Activities,Stay Connected,all,,,http://www.youtube.com/user/uwhuskies
Events and Activities,Stay Connected,all,,,https://www.instagram.com/uofwa/,UW Instagram,https://instagram.com/UW_BOTHELL,UW Bothell Instagram,https://www.instagram.com/uwtacoma,UW Tacoma Instagram,no
Events and Activities,Stay Connected,all,,,https://twitter.com/UW,UW Twitter,https://twitter.com/UWBothell,UW Bothell Twitter,https://twitter.com/uwtacoma,UW Tacoma Twitter,no
Events and Activities,Stay Connected,all,,,https://www.linkedin.com/company/university-of-washington,UW LinkedIn,https://www.linkedin.com/groups/University-Washington-Bothell-92858?home=&gid=92858,UW Bothell LinkedIn,https://www.linkedin.com/school/university-of-washington-tacoma/,UW Tacoma LinkedIn,no
-Events and Activities,Student Clubs and Organizations,all,,,https://hub.washington.edu/sao/rso-directory/,Registered Student Organizations (RSO) Directory,http://www.bothell.washington.edu/sea/clubs/join,Club Directory,https://dawgden.tacoma.uw.edu/Organizations,UW Tacoma Clubs and Organizations,no
+Events and Activities,Student Clubs and Organizations,all,,,https://hub.washington.edu/sao/rso-directory/,Registered Student Organizations (RSO) Directory,https://uwb.presence.io/organizations,Club Directory,https://dawgden.tacoma.uw.edu/Organizations,UW Tacoma Clubs and Organizations,no
Libraries,Library Resources,all,,,http://lib.washington.edu/,UW Libraries,http://library.uwb.edu/,UW Bothell Library,http://www.tacoma.uw.edu/library,UW Tacoma Library,no
Libraries,Library Resources,all,http://search.lib.uw.edu/account,Your Library Account,,,,,,,no
Libraries,Library Resources,all,http://guides.lib.uw.edu/az.php,Articles and Research Databases,,,,,,,no
@@ -165,7 +164,6 @@ Research,"Research Centers, Shared Facilities, and Equipment",all,https://escien
Research,Research Data and Publishing,all,https://www.lib.washington.edu/services/faculty,Getting Started with Library Research,,,,,,,no
Research,Research Data and Publishing,all,https://www.lib.washington.edu/dataservices/tools/services,Tools for Data-Related Research,,,,,,,no
Research,Research Data and Publishing,all,https://guides.lib.uw.edu/research/dmg,Research Data Services,,,,,,,no
-Research,Research Data and Publishing,all,https://hsl.uw.edu/trail/,Translational Research and Information Lab (TRAIL),,,,,,,no
Research,Research Data and Publishing,all,,,https://www.lib.washington.edu/openscholarship,"Open Scholarship Commons: Tools, Services, Consultations",https://guides.lib.uw.edu/bothell/digitalscholarship,Digital Scholarship,https://guides.lib.uw.edu/tacdigitalscholarship,Digital Scholarship,no
Research,Research Data and Publishing,all,https://guides.lib.uw.edu/research/spoa,Scholarly Publishing and Open Access,,,,,,,no
Research,Technical Support,all,https://itconnect.uw.edu/help/,UW-IT Technical Support,,,,,,,no
diff --git a/myuw/management/commands/db_cleanup.py b/myuw/management/commands/db_cleanup.py
index a7c5cfe323..44747b6cc6 100644
--- a/myuw/management/commands/db_cleanup.py
+++ b/myuw/management/commands/db_cleanup.py
@@ -14,7 +14,7 @@
from uw_sws import sws_now, SWS_TIMEZONE
from myuw.models import (
VisitedLinkNew, SeenRegistration, UserNotices, UserCourseDisplay)
-from myuw.dao.term import get_term_by_date, get_term_before
+from myuw.dao.term import get_term_by_date, get_term_before, get_term_after
from myuw.util.settings import get_cronjob_recipient, get_cronjob_sender
from myuw.logger.timer import Timer
@@ -65,11 +65,19 @@ def deletion(self, ids_to_delete, queryf):
["{}@uw.edu".format(get_cronjob_recipient())])
raise CommandError(msg)
+ def get_cur_term(self):
+ comparison_date = sws_now().date()
+ term = get_term_by_date(comparison_date)
+ # Match MyUW quarter switchS
+ if comparison_date > term.grade_submission_deadline.date():
+ return get_term_after(term)
+ return term
+
def course_display(self):
# clean up after one year
timer = Timer()
queryf = "DELETE FROM user_course_display_pref WHERE id IN ({})"
- term = get_term_by_date(sws_now().date())
+ term = self.get_cur_term()
y = term.year - 1
q = term.quarter
qset = UserCourseDisplay.objects.filter(year=y, quarter=q)
@@ -88,7 +96,7 @@ def notice_read(self):
# clean up after 180 days
timer = Timer()
queryf = "DELETE FROM myuw_mobile_usernotices WHERE id IN ({})"
- cut_off_dt = self.get_cut_off_date()
+ cut_off_dt = self.get_cut_off_date(90)
qset = UserNotices.objects.filter(first_viewed__lt=cut_off_dt)
if qset.exists():
ids_to_delete = qset.values_list('id', flat=True)
@@ -105,7 +113,7 @@ def registration_seen(self):
# clean up previous quarters'
timer = Timer()
queryf = "DELETE FROM myuw_mobile_seenregistration WHERE id IN ({})"
- term = get_term_before(get_term_by_date(sws_now().date()))
+ term = get_term_before(self.get_cur_term())
qset = SeenRegistration.objects.filter(
year=term.year, quarter=term.quarter)
if qset.exists():
@@ -123,7 +131,7 @@ def link_visited(self):
# clean up after 180 days
timer = Timer()
queryf = "DELETE FROM myuw_visitedlinknew WHERE id IN ({})"
- cut_off_dt = self.get_cut_off_date()
+ cut_off_dt = self.get_cut_off_date(90)
qset = VisitedLinkNew.objects.filter(visit_date__lt=cut_off_dt)
if qset.exists():
ids_to_delete = qset.values_list('id', flat=True)
diff --git a/myuw/resources/book/file/uw/iacourse_status.json_regid_12345678901234567890123456789012 b/myuw/resources/book/file/uw/iacourse_status.json_regid_12345678901234567890123456789012
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/myuw/resources/book/file/uw/iacourse_status.json_regid_12345678901234567890123456789012
@@ -0,0 +1 @@
+{}
diff --git a/myuw/resources/book/file/uw/iacourse_status.json_regid_9136CCB8F66711D5BE060004AC494FFE b/myuw/resources/book/file/uw/iacourse_status.json_regid_9136CCB8F66711D5BE060004AC494FFE
new file mode 100644
index 0000000000..d0994d791a
--- /dev/null
+++ b/myuw/resources/book/file/uw/iacourse_status.json_regid_9136CCB8F66711D5BE060004AC494FFE
@@ -0,0 +1,76 @@
+[
+ {
+ "Year": 2013,
+ "Quarter": "spring",
+ "IACourses": [
+ {
+ "SLN": 13833,
+ "BookstoreCourseID": 1000111,
+ "DigitalItems": [
+ {
+ "ISBN": "9781256396362",
+ "ItemInternalID": 500555,
+ "OptsOutStatus": false,
+ "Paid": true,
+ "Price": 99.00
+ }
+ ]
+ },
+ {
+ "SLN": 18532,
+ "BookstoreCourseID": 1000133,
+ "DigitalItems": [
+ {
+ "ISBN": "9780878935970",
+ "ItemInternalID": 3000366,
+ "OptsOutStatus": true,
+ "Paid": false,
+ "Price": 96.59
+ }
+ ]
+ },
+ {
+ "SLN": 13830,
+ "BookstoreCourseID": 1000157,
+ "DigitalItems": [
+ {
+ "ISBN": "9781256396362",
+ "ItemInternalID": 2000268,
+ "OptsOutStatus": false,
+ "Paid": false,
+ "Price": 24.26
+ }
+ ]
+ }
+ ],
+ "BalanceToPay": 219.85,
+ "PaymentDeadline": "2013-04-19",
+ "BookstoreDigitalMaterialLink": "https://www.ubookstore.com/materials?idset=500555,3000366,2000268",
+ "BookstoreCheckOutLink": "https://www.ubookstore.com/app/site/query/additemtocart.nl?n=3&buyid=multi&multi=500555,1;3000366,1;2000268,1;",
+ "LastUpdated": "2013-2-09T00:51:09.769370-08:00"
+ },
+ {
+ "Year": 2013,
+ "Quarter": "summer",
+ "IACourses": [
+ {
+ "SLN": 13833,
+ "BookstoreCourseID": 1000111,
+ "DigitalItems": [
+ {
+ "ISBN": "9781256396062",
+ "ItemInternalID": 500555,
+ "OptsOutStatus": false,
+ "Paid": true,
+ "Price": 99.00
+ }
+ ]
+ }
+ ],
+ "BalanceToPay": 99.00,
+ "PaymentDeadline": "2013-07-12",
+ "BookstoreDigitalMaterialLink": "https://www.ubookstore.com/materials?idset=500555",
+ "BookstoreCheckOutLink": "https://www.ubookstore.com/app/site/query/additemtocart.nl?n=3&buyid=multi&multi=500555,1;",
+ "LastUpdated": "2013-2-09T00:51:09.769370-08:00"
+ }
+]
diff --git a/myuw/templates/restclients/customform/book/iacourse.html b/myuw/templates/restclients/customform/book/iacourse.html
new file mode 100644
index 0000000000..8bd96f168b
--- /dev/null
+++ b/myuw/templates/restclients/customform/book/iacourse.html
@@ -0,0 +1,11 @@
+
University Bookstore Inclusive Access Course Materials
+{% include "restclients/customform/_common/regid.html" %}
+
+Regids:
+
+ - javerage: 9136CCB8F66711D5BE060004AC494FFE
+ - jbothell: FE36CCB8F66711D5BE060004AC494FCD
+ - jinter: 9136CCB8F66711D5BE060004AC494F31
+ - jnew: FE36CCB8F66711D5BE060004AC494F31
+ - eight: 12345678901234567890123456789012
+
diff --git a/myuw/templates/supporttools/custom_sidebar_links.html b/myuw/templates/supporttools/custom_sidebar_links.html
index ae3ca84075..c63ec7fd77 100644
--- a/myuw/templates/supporttools/custom_sidebar_links.html
+++ b/myuw/templates/supporttools/custom_sidebar_links.html
@@ -141,6 +141,13 @@ Web Services
{% endif %}
+ {% url 'myuw_rest_search' 'book' 'iacourse.html' as iacourse_url %}
+ {% if iacourse_url %}
+
+ IA Courses
+
+ {% endif %}
+
{% url 'myuw_rest_search' 'upass' 'index.html' as upass_url %}
{% if upass_url %}
diff --git a/myuw/test/api/test_academic_calendar.py b/myuw/test/api/test_academic_calendar.py
index 63300cb87e..b9601afd39 100644
--- a/myuw/test/api/test_academic_calendar.py
+++ b/myuw/test/api/test_academic_calendar.py
@@ -55,7 +55,7 @@ def test_muwm_2489(self):
# test MUWM_4485,
follow_link = urlopen(data[0]["event_url"])
- self.assertEquals(follow_link.reason, "OK")
+ self.assertEquals(follow_link.reason, "")
def test_current_events(self):
self.set_user('javerage')
diff --git a/myuw/test/api/test_textbook.py b/myuw/test/api/test_textbook.py
index 4d3095b106..d51eb9f0d9 100644
--- a/myuw/test/api/test_textbook.py
+++ b/myuw/test/api/test_textbook.py
@@ -2,9 +2,9 @@
# SPDX-License-Identifier: Apache-2.0
import json
+from myuw.views.api.textbook import get_payment_quarter
+from myuw.test import get_request_with_date
from myuw.test.api import MyuwApiTest, require_url, fdao_bookstore_override
-from myuw.test import get_request_with_user, get_request_with_date
-
VERBACOMPARE_URL_PREFIX = 'http://uw-seattle.verbacompare.com'
IMAGE_URL_PREFIX = 'www7.bookstore.washington.edu/MyUWImage.taf'
@@ -14,7 +14,7 @@
class TestApiBooks(MyuwApiTest):
'''Tests textbooks api'''
- @require_url('myuw_home')
+ @require_url('myuw_book_api')
def test_javerage_books(self):
self.set_user('javerage')
response = self.get_response_by_reverse(
@@ -53,3 +53,64 @@ def test_no_sche_books(self):
'quarter': 'spring',
'summer_term': ''})
self.assertEquals(response.status_code, 200)
+
+ @require_url('myuw_iacourse_digital_material_api')
+ def test_digital_material_api(self):
+ self.set_user('javerage')
+ response = self.get_response_by_reverse(
+ 'myuw_iacourse_digital_material')
+ self.assertEquals(response.status_code, 200)
+ data = json.loads(response.content)
+ self.assertEquals(data["quarter"], "spring")
+ self.assertEquals(data["year"], 2013)
+ self.assertEquals(data["balance"], 219.85)
+
+ response = self.get_response_by_reverse(
+ 'myuw_iacourse_digital_material_api',
+ kwargs={'year': 2013,
+ 'quarter': 'spring'})
+ self.assertEquals(response.status_code, 200)
+ data = json.loads(response.content)
+ self.assertEquals(data["quarter"], "spring")
+ self.assertEquals(data["year"], 2013)
+ self.assertEquals(data["balance"], 219.85)
+
+ response = self.get_response_by_reverse(
+ 'myuw_iacourse_digital_material_api',
+ kwargs={'year': 2013,
+ 'quarter': 'autumn'})
+ self.assertEquals(response.status_code, 404)
+
+ response = self.get_response_by_reverse(
+ 'myuw_iacourse_digital_material_api',
+ kwargs={'year': 2013,
+ 'quarter': 'winter'})
+ self.assertEquals(response.status_code, 404)
+
+ self.set_user('jbothell')
+ response = self.get_response_by_reverse(
+ 'myuw_iacourse_digital_material_api',
+ kwargs={'year': 2013,
+ 'quarter': 'spring'})
+ self.assertEquals(response.status_code, 404)
+
+ response = self.get_response_by_reverse(
+ 'myuw_iacourse_digital_material')
+ self.assertEquals(response.status_code, 404)
+
+ def test_get_payment_quarter(self):
+ request = get_request_with_date('2013-06-18')
+ q = get_payment_quarter(request)
+ self.assertEquals(q.quarter, "spring")
+
+ request = get_request_with_date('2013-06-19')
+ q = get_payment_quarter(request)
+ self.assertEquals(q.quarter, "summer")
+
+ request = get_request_with_date('2013-09-19')
+ q = get_payment_quarter(request)
+ self.assertEquals(q.quarter, "autumn")
+
+ request = get_request_with_date('2013-12-27')
+ q = get_payment_quarter(request)
+ self.assertEquals(q.quarter, "winter")
diff --git a/myuw/test/dao/test_textbook.py b/myuw/test/dao/test_textbook.py
index 83a4e09705..7350077592 100644
--- a/myuw/test/dao/test_textbook.py
+++ b/myuw/test/dao/test_textbook.py
@@ -6,7 +6,8 @@
from uw_sws.models import Term
from restclients_core.exceptions import DataFailureException
from myuw.dao.textbook import (
- get_textbook_by_schedule, get_order_url_by_schedule)
+ get_textbook_by_schedule, get_order_url_by_schedule,
+ get_iacourse_status)
from myuw.dao.instructor_schedule import get_instructor_schedule_by_term
from myuw.dao.term import get_current_quarter
from myuw.test import get_request_with_user, get_request_with_date
@@ -58,3 +59,22 @@ def test_get_uwt_inst_textbook(self):
self.assertEqual(len(schedule.sections), 3)
books = get_textbook_by_schedule(schedule)
self.assertEquals(len(books), 0)
+
+ def test_get_iacourse_status(self):
+ req = get_request_with_user(
+ 'javerage', get_request_with_date("2013-05-01"))
+ term = get_current_quarter(req)
+ data = get_iacourse_status(req, term)
+ self.assertIsNotNone(data.json_data())
+
+ req = get_request_with_user(
+ 'javerage', get_request_with_date("2013-06-25"))
+ term = get_current_quarter(req)
+ data = get_iacourse_status(req, term)
+ self.assertIsNotNone(data.json_data())
+
+ req = get_request_with_user(
+ 'javerage', get_request_with_date("2013-12-31"))
+ term = get_current_quarter(req)
+ data = get_iacourse_status(req, term)
+ self.assertIsNone(data)
diff --git a/myuw/urls.py b/myuw/urls.py
index 92b4d7f8fe..0c3315a1cf 100644
--- a/myuw/urls.py
+++ b/myuw/urls.py
@@ -36,13 +36,13 @@
LTIInstSectionDetails)
from myuw.views.api.instructor_schedule import (InstScheCurQuar, InstScheQuar,
InstSect)
-from myuw.views.api.instructor_section_display import \
- CloseMinicard, PinMinicard
+from myuw.views.api.instructor_section_display import (
+ CloseMinicard, PinMinicard)
from myuw.views.api.finance import Finance
from myuw.views.api.hfs import HfsBalances
from myuw.views.api.future_schedule import StudClasScheFutureQuar
-from myuw.views.api.prev_unfinished_schedule import \
- StudUnfinishedPrevQuarClasSche
+from myuw.views.api.prev_unfinished_schedule import (
+ StudUnfinishedPrevQuarClasSche)
from myuw.views.api.grad import MyGrad
from myuw.views.api.iasystem import IASystem
from myuw.views.api.library import MyLibInfo
@@ -50,7 +50,8 @@
from myuw.views.api.profile import MyProfile
from myuw.views.api.category_links import CategoryLinks
from myuw.views.api.other_quarters import RegisteredFutureQuarters
-from myuw.views.api.textbook import Textbook, TextbookCur
+from myuw.views.api.textbook import (
+ Textbook, TextbookCur, IACDigitalItemsCur, IACDigitalItems)
from myuw.views.api.notices import Notices
from myuw.views.api.myplan import MyPlan
from myuw.views.api.academic_events import AcademicEvents
@@ -62,8 +63,8 @@
from myuw.views.api.directory import MyDirectoryInfo
from myuw.views.lti.photo_list import LTIPhotoList
from myuw.views.api.visual_schedule import VisSchedCurQtr, VisSchedOthrQtr
-from myuw.views.api.hx_toolkit import HxToolkitMessage, HxToolkitWeekMessage, \
- HxToolkitMessageList
+from myuw.views.api.hx_toolkit import (
+ HxToolkitMessage, HxToolkitWeekMessage, HxToolkitMessageList)
from myuw.views.api.resources import (ResourcesList,
ResourcesPin,
PinnedResources)
@@ -146,6 +147,12 @@
r'(?P[-,fulabterm]*)$',
Textbook.as_view(),
name="myuw_book_api"),
+ re_path(r'^api/v1/iacourse/(?P\d{4}),(?P[a-z]+)',
+ IACDigitalItems.as_view(),
+ name="myuw_iacourse_digital_material_api"),
+ re_path(r'^api/v1/iacourse/current/?$',
+ IACDigitalItemsCur.as_view(),
+ name="myuw_iacourse_digital_material"),
re_path(r'^api/v1/categorylinks/(?P.*?)$',
CategoryLinks.as_view(),
name="myuw_links_api"),
diff --git a/myuw/util/cache.py b/myuw/util/cache.py
index 8b08e2de44..25a22ff490 100644
--- a/myuw/util/cache.py
+++ b/myuw/util/cache.py
@@ -17,6 +17,7 @@ class MyUWMemcachedCache(RestclientPymemcacheClient):
def get_cache_expiration_time(self, service, url, status=None):
if "myplan_auth" == service:
return FIFTEEN_MINS * 3
+
if "myplan" == service:
return FIVE_SECONDS
diff --git a/myuw/views/api/textbook.py b/myuw/views/api/textbook.py
index 2c5fe31b0a..bd623c1734 100644
--- a/myuw/views/api/textbook.py
+++ b/myuw/views/api/textbook.py
@@ -1,17 +1,19 @@
# Copyright 2023 UW-IT, University of Washington
# SPDX-License-Identifier: Apache-2.0
+from datetime import timedelta
import logging
import traceback
from restclients_core.exceptions import DataFailureException
from myuw.dao.registration import get_schedule_by_term
from myuw.dao.instructor_schedule import get_instructor_schedule_by_term
-from myuw.dao.term import get_specific_term, get_current_quarter
+from myuw.dao.term import (
+ get_specific_term, get_comparison_date, get_current_quarter, get_term_after)
from myuw.dao.textbook import (
- get_textbook_by_schedule, get_order_url_by_schedule)
+ get_textbook_by_schedule, get_order_url_by_schedule,
+ get_iacourse_status)
from myuw.logger.timer import Timer
-from myuw.logger.logresp import (
- log_api_call, log_msg, log_data_not_found_response)
+from myuw.logger.logresp import log_api_call
from myuw.views import prefetch_resources
from myuw.views.api import ProtectedAPI
from myuw.views.error import handle_exception, data_not_found
@@ -104,3 +106,58 @@ def get(self, request, *args, **kwargs):
timer, request, get_current_quarter(request), None)
except Exception:
return handle_exception(logger, timer, traceback)
+
+
+class IACDigitalItems(ProtectedAPI):
+ # MUWM-5272
+
+ def get(self, request, *args, **kwargs):
+ """
+ myuw_iacourse_digital_material_api
+ GET returns 200 with textbooks for the given quarter
+ """
+ timer = Timer()
+ year = kwargs.get("year")
+ quarter = kwargs.get("quarter")
+ try:
+ ret_obj = get_iacourse_status(
+ request, get_specific_term(year, quarter))
+ if ret_obj is None:
+ return data_not_found()
+ return self.json_response(ret_obj.json_data())
+ except Exception:
+ return handle_exception(logger, timer, traceback)
+ finally:
+ log_api_call(
+ timer, request, "IACourse_Status {}.{}".format(
+ year, quarter))
+
+
+class IACDigitalItemsCur(ProtectedAPI):
+ # MUWM-5272
+
+ def get(self, request, *args, **kwargs):
+ """
+ myuw_iacourse_digital_material
+ GET returns 200 with textbooks for the given quarter
+ """
+ timer = Timer()
+ try:
+ ret_obj = get_iacourse_status(
+ request, get_payment_quarter(request))
+ if ret_obj is None:
+ return data_not_found()
+ return self.json_response(ret_obj.json_data())
+ except Exception:
+ return handle_exception(logger, timer, traceback)
+ finally:
+ log_api_call(timer, request, "IACDigitalItemsCur")
+
+
+def get_payment_quarter(request):
+ term = get_current_quarter(request)
+ term_after = get_term_after(term)
+ comparison_date = get_comparison_date(request)
+ if comparison_date > term_after.first_day_quarter - timedelta(days=6):
+ return term_after
+ return term
diff --git a/myuw/views/rest_search.py b/myuw/views/rest_search.py
index a21617a656..3dfe380c0d 100644
--- a/myuw/views/rest_search.py
+++ b/myuw/views/rest_search.py
@@ -23,11 +23,15 @@ def get_proxy_url(self, request, service, url):
service, url, request.POST))
if service == "book":
- url = "uw/json_utf8_202007.ubs"
- url = "{}?quarter={}&sln1={}&returnlink=t".format(
- "uw/json_utf8_202007.ubs",
- request.POST["quarter"],
- request.POST["sln1"])
+ if "iacourse" == url:
+ url = "uw/iacourse_status.json?regid={}".format(
+ request.POST["uwregid"])
+ else:
+ url = "uw/json_utf8_202007.ubs"
+ url = "{}?quarter={}&sln1={}&returnlink=t".format(
+ "uw/json_utf8_202007.ubs",
+ request.POST["quarter"],
+ request.POST["sln1"])
elif service == "grad":
params = self.format_params(request)
elif service == "hfs":
diff --git a/myuw_vue/accounts.js b/myuw_vue/accounts.js
index 5e6b88e17f..6b6e566408 100644
--- a/myuw_vue/accounts.js
+++ b/myuw_vue/accounts.js
@@ -22,6 +22,7 @@ import profile from './vuex/store/profile';
import upass from './vuex/store/upass';
import tuition from './vuex/store/tuition';
import notices from './vuex/store/notices';
+import iac from './vuex/store/iacourse-digital-material';
vueConf.store.registerModule('hfs', hfs);
vueConf.store.registerModule('library', library);
@@ -29,6 +30,7 @@ vueConf.store.registerModule('profile', profile);
vueConf.store.registerModule('upass', upass);
vueConf.store.registerModule('tuition', tuition);
vueConf.store.registerModule('notices', notices);
+vueConf.store.registerModule('iac', iac);
vueConf.store.commit('addVarToState', {
name: 'page',
diff --git a/myuw_vue/components/accounts/tuition-fees.vue b/myuw_vue/components/accounts/tuition-fees.vue
index e9c0de6c4a..9cbb78842c 100644
--- a/myuw_vue/components/accounts/tuition-fees.vue
+++ b/myuw_vue/components/accounts/tuition-fees.vue
@@ -19,7 +19,7 @@
@@ -40,18 +40,15 @@
- Make payment
-
+ >Make payment
@@ -60,17 +57,14 @@
Account Credit
-
- +${{ Math.abs(tuiBalance).toFixed(2) }} CR
-
+ +${{ Math.abs(tuiBalance).toFixed(2) }} CR
Student Fiscal Services
- No payment needed
-
- Tuition Statement
-
+ No payment needed
+
Tuition Statement
@@ -90,9 +84,7 @@
- Make payment
-
+ >Make payment
@@ -107,7 +99,8 @@
PCE-Continuum College
-
Account Statement
@@ -130,14 +123,64 @@
+
+
+ UW Day One Access Fees
+
+
+ One or more of your enrolled courses provides you access to
+ required digital materials,
+ in Canvas, on or before the first day of class.
+
+
+ To maintain access to these materials at Day One Access pricing,
+ you must pay for these materials.
+ About the Day One Access Program.
+
+
+
+ Amount Due
+
+ ${{ iacData.balance.toFixed(2) }}
+
+ $ 0
+
+
+
+
University Book Store
+
+ Make payment
+
+
+
+
+
+
+
+
+ Payment Due
+
+
+
+
+
+ {{ dayOneAccessDueDateFromNow }}
+
+
+
+
- Give access to your tuition account and financial aid information
-
+ >Give access to your tuition account and financial aid information
to parents or other third parties.
@@ -163,13 +206,13 @@
An error occurred and MyUW cannot load your information right now. In the meantime, try the
- Tuition Statement page.
- PCE Tuition portal.
@@ -180,9 +223,7 @@
-
+
diff --git a/myuw_vue/components/textbooks/book.vue b/myuw_vue/components/textbooks/book.vue
index b172a0b7e3..fc77f0ba51 100644
--- a/myuw_vue/components/textbooks/book.vue
+++ b/myuw_vue/components/textbooks/book.vue
@@ -11,9 +11,7 @@
+ -
+ DIGITAL MATERIAL
+
+ -
+ Paid
+ Opted out
+ Payment due
+
-
{{ book.authors > 1 ? "Authors" : "Author" }}
@@ -36,10 +42,16 @@
- Price
-
- ${{ book.lowest_price.toFixed(2) }}
- to
- ${{ book.highest_price.toFixed(2) }}
+
+ ${{ book.lowest_price.toFixed(2) }}
+ to
+ ${{ book.highest_price.toFixed(2) }}
+
+
+ ${{ book.lowest_price.toFixed(2) }}
+
+ Digital: ${{ digitalItem.price.toFixed(2) }}
Visit
@@ -67,14 +79,57 @@
diff --git a/myuw_vue/tests/mock_data/textbooks/javerage-iac-2013-spr.json b/myuw_vue/tests/mock_data/textbooks/javerage-iac-2013-spr.json
new file mode 100644
index 0000000000..6a17857cdb
--- /dev/null
+++ b/myuw_vue/tests/mock_data/textbooks/javerage-iac-2013-spr.json
@@ -0,0 +1,50 @@
+{
+ "quarter": "spring",
+ "year": 2013,
+ "balance": 219.85,
+ "payment_due_day": "2013-04-19",
+ "bookstore_digital_material_url": "https://www.ubookstore.com/materials?idset=500555,3000366,2000268",
+ "bookstore_checkout_url": "https://www.ubookstore.com/app/site/query/additemtocart.nl?n=3&buyid=multi&multi=500555,1;3000366,1;2000268,1;",
+ "last_updated": "2013-02-09T00:51:09.769370-08:00",
+ "ia_courses": {
+ "13833": {
+ "sln": 13833,
+ "bookstore_course_id": 1000111,
+ "digital_items": {
+ "9781256396362": {
+ "isbn": "9781256396362",
+ "bookstore_item_id": 500555,
+ "opt_out_status": false,
+ "paid": true,
+ "price": 99.0
+ }
+ }
+ },
+ "18532": {
+ "sln": 18532,
+ "bookstore_course_id": 1000133,
+ "digital_items": {
+ "9780878935970": {
+ "isbn": "9780878935970",
+ "bookstore_item_id": 3000366,
+ "opt_out_status": true,
+ "paid": false,
+ "price": 96.59
+ }
+ }
+ },
+ "13830": {
+ "sln": 13830,
+ "bookstore_course_id": 1000157,
+ "digital_items": {
+ "9781256396362": {
+ "isbn": "9781256396362",
+ "bookstore_item_id": 2000268,
+ "opt_out_status": false,
+ "paid": false,
+ "price": 24.26
+ }
+ }
+ }
+ }
+}
diff --git a/myuw_vue/tests/textbooks.test.js b/myuw_vue/tests/textbooks.test.js
index 5bc7eef608..ae4afc1e7d 100644
--- a/myuw_vue/tests/textbooks.test.js
+++ b/myuw_vue/tests/textbooks.test.js
@@ -12,7 +12,9 @@ import LinkButton from '../components/_templates/link-button.vue';
import stud_schedule from '../vuex/store/schedule/student';
import inst_schedule from '../vuex/store/schedule/instructor';
import textbooks from '../vuex/store/textbooks';
+import iac from '../vuex/store/iacourse-digital-material';
+import javgBook from './mock_data//textbooks/javerage-iac-2013-spr.json';
import mockStudCourses from
'./mock_data/stud_schedule/javerage2013Spring.json';
import mockStudTextbook from './mock_data/textbooks/javerage-2013-spr.json';
@@ -41,11 +43,14 @@ describe('Textbook cards', () => {
stud_schedule,
inst_schedule,
textbooks,
+ iac,
},
state: {
user: {
affiliations: {
seattle: true,
+ student: true,
+ bothell: true,
}
}
}
@@ -58,11 +63,12 @@ describe('Textbook cards', () => {
'/api/v1/book/2013,spring': mockStudTextbook,
'/api/v1/schedule/2013,spring': mockStudCourses,
'/api/v1/instructor_schedule/2013,spring': mockInstSche,
+ '/api/v1/iacourse/2013,spring': javgBook,
};
return Promise.resolve({data: urlData[url]});
});
- const wrapper = mount(Textbooks, {store, localVue,
+ let wrapper = mount(Textbooks, {store, localVue,
propsData: {'term': '2013,spring'}});
await new Promise(setImmediate);
expect(wrapper.vm.term).toEqual('2013,spring');
@@ -81,6 +87,13 @@ describe('Textbook cards', () => {
expect(bookData.enrolledSections.length).toBe(5);
expect(bookData.sections.length).toBe(11);
expect(bookData.hasTeachingSections).toBe(true);
+
+ const section5 = wrapper.vm.bookData.enrolledSections[4];
+ wrapper = mount(Book, {
+ store, localVue,
+ propsData: { 'book': section5.books[0], 'sln': section5.sln }
+ });
+ expect(wrapper.vm.digitalItem).toBeTruthy();
});
it('Verify other campus courses', async () => {
@@ -91,6 +104,7 @@ describe('Textbook cards', () => {
const urlData = {
'/api/v1/book/2013,spring': mockTextbook,
'/api/v1/schedule/2013,spring': mockStudCourses,
+ '/api/v1/iacourse/2013,spring': javgBook,
};
return Promise.resolve({data: urlData[url]});
});
diff --git a/myuw_vue/tests/tuition.test.js b/myuw_vue/tests/tuition.test.js
index b3ce5e1da6..7c2c27bbe4 100644
--- a/myuw_vue/tests/tuition.test.js
+++ b/myuw_vue/tests/tuition.test.js
@@ -6,12 +6,15 @@ import Vuex from 'vuex';
import {createLocalVue} from './helper';
import tuition from '../vuex/store/tuition';
import notices from '../vuex/store/notices';
+import iac from '../vuex/store/iacourse-digital-material';
+
import TuitionFees from '../components/accounts/tuition-fees.vue';
import TuitionRes from '../components/accounts/tuition-resources.vue';
import CardStatus from '../components/_templates/card-status.vue';
import LinkButton from '../components/_templates/link-button.vue';
import FinAid from '../components/_common/finaid.vue';
+import javgBook from './mock_data//textbooks/javerage-iac-2013-spr.json';
import jbotTuition from './mock_data/tuition/jbothell.json';
import jbotNotices from './mock_data/notice/jbothell.json';
import javgTuition from './mock_data/tuition/javerage.json';
@@ -29,6 +32,7 @@ describe('Tuition store', () => {
modules: {
tuition,
notices,
+ iac,
},
state: {
cardDisplayDates: {
@@ -40,6 +44,8 @@ describe('Tuition store', () => {
pce: false,
grad_c2: false,
undergrad_c2: false,
+ bothell: true,
+ seattle: true,
}
}
}
@@ -48,6 +54,9 @@ describe('Tuition store', () => {
it('Evaluate the computed properties of jbothell', async () => {
axios.get.mockImplementation((url) => {
+ if (url === '/api/v1/iacourse/current') {
+ return Promise.reject({ response: { status: 404 } });
+ }
const urlData = {
'/api/v1/notices/': jbotNotices,
'/api/v1/finance/': jbotTuition,
@@ -59,6 +68,9 @@ describe('Tuition store', () => {
expect(wrapper.vm.isStudent).toBe(true);
expect(wrapper.vm.isC2Grad).toBe(false);
+ expect(wrapper.vm.seaStud).toBe(true);
+ expect(wrapper.vm.botStud).toBe(true);
+ expect(wrapper.vm.hasIacData).toBe(undefined);
expect(wrapper.vm.isC2).toBe(false);
expect(wrapper.vm.isPCE).toBe(false);
expect(wrapper.vm.notices.length).toBe(14);
@@ -84,6 +96,7 @@ describe('Tuition store', () => {
const urlData = {
'/api/v1/notices/': javgNotices,
'/api/v1/finance/': javgTuition,
+ '/api/v1/iacourse/current': javgBook,
};
return Promise.resolve({data: urlData[url]});
});
@@ -104,11 +117,16 @@ describe('Tuition store', () => {
expect(wrapper.findComponent(LinkButton).exists()).toBe(true);
expect(wrapper.findComponent(FinAid).exists()).toBe(true);
expect(wrapper.findComponent(TuitionRes).exists()).toBe(true);
- expect(wrapper.findAllComponents(CardStatus).length).toBe(3);
+ expect(wrapper.findAllComponents(CardStatus).length).toBe(5);
+ expect(wrapper.vm.hasIacData).toBeTruthy;
+ expect(wrapper.vm.dayOneAccessDueDateFromNow).toBeTruthy;
});
it('Evaluate the computed properties of jpce', async () => {
axios.get.mockImplementation((url) => {
+ if (url === '/api/v1/iacourse/current') {
+ return Promise.reject({ response: { status: 404 } });
+ }
const urlData = {
'/api/v1/notices/': gc2Notices,
'/api/v1/finance/': gc2Tuition,
@@ -120,5 +138,6 @@ describe('Tuition store', () => {
expect(wrapper.vm.hasTuitionDate).toBe(true);
expect(wrapper.vm.pceBalance).toBe(2897.00);
expect(wrapper.vm.tuiBalance).toBe(-10.00);
+ expect(wrapper.vm.hasIacData).toBe(undefined);
});
});
diff --git a/myuw_vue/textbooks.js b/myuw_vue/textbooks.js
index c75f4e4114..d1c943fd3b 100644
--- a/myuw_vue/textbooks.js
+++ b/myuw_vue/textbooks.js
@@ -9,10 +9,12 @@ import Textbooks from './components/textbooks/textbooks.vue';
import instSchedule from './vuex/store/schedule/instructor';
import studSchedule from './vuex/store/schedule/student';
import textbooks from './vuex/store/textbooks';
+import iac from './vuex/store/iacourse-digital-material';
vueConf.store.registerModule('inst_schedule', instSchedule);
vueConf.store.registerModule('stud_schedule', studSchedule);
vueConf.store.registerModule('textbooks', textbooks);
+vueConf.store.registerModule('iac', iac);
vueConf.store.commit('addVarToState', {
name: 'page',
diff --git a/myuw_vue/vuex/store/iacourse-digital-material.js b/myuw_vue/vuex/store/iacourse-digital-material.js
new file mode 100644
index 0000000000..4501d317a3
--- /dev/null
+++ b/myuw_vue/vuex/store/iacourse-digital-material.js
@@ -0,0 +1,10 @@
+import { fetchBuilder, extractData, buildWith } from './model_builder';
+
+
+const customActions = {
+ fetch: fetchBuilder('/api/v1/iacourse/', extractData, 'json'),
+};
+
+export default buildWith(
+ { customActions }
+);
diff --git a/setup.py b/setup.py
index a97c4b754b..990cb074b0 100644
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@
'UW-Django-SAML2~=1.7',
'aws-message-client~=1.5',
'UW-RestClients-Core~=1.4',
- 'UW-RestClients-Bookstore~=1.2',
+ 'UW-RestClients-Bookstore~=1.3',
'UW-RestClients-Canvas~=1.2',
'UW-RestClients-CoDa~=1.0',
'UW-RestClients-Grad~=1.1',
@@ -59,7 +59,7 @@
'Django-Persistent-Message~=1.3',
'Django-Safe-EmailBackend~=1.2',
'django_client_logger<3.0',
- 'UW-HX-Toolkit~=2.7.8',
+ 'UW-HX-Toolkit~=2.7',
'django-blti~=2.2',
'nh3',
'pyyaml',