class TestMemberServicesIntegration(unittest.TestCase): """Unit test for the MemberService type """ layer = INTEGRATION_TESTING def setUp(self): self.portal = self.layer['portal'] self.services = self.portal._getOb('products_and_services') self.intids = queryUtility(IIntIds, context=self.portal) self.dao = MemberServicesDataAccess(self.portal) setRoles(self.portal, TEST_USER_ID, ['Member']) self.session = SESSION() def tearDown(self): count = self.session.query(MemberService).filter( MemberService.memberid == TEST_USER_ID ).delete() print 'Deleted %s' % count transaction.commit() def test_get_all_memberservices(self): db_ms = self.session.query(MemberService).all() all_ms = self.dao.get_all_memberservices() self.assertEquals(len(db_ms), len(all_ms)) self.assertEquals(all_ms, db_ms) def test_get_memberservices(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.get_memberservice(ms1.id) service_uids = [self.intids.getId(service),] memberid = TEST_USER_ID memberservices = \ self.dao.get_memberservices(service_uids, memberid) self.assertEquals(memberservices, [ms1_db]) def test_get_memberservices_by_subject(self): maths_services = \ [s for s in self.services.objectValues() if 'maths' in s.subject] for count in range(0,3): service = maths_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) self.dao.add_memberservice(**ms_args) self.session.flush() science_services = \ [s for s in self.services.objectValues() if 'science' in s.subject] for count in range(0,3): service = science_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_memberservices_by_subject(TEST_USER_ID, 'maths') self.assertEqual(len(memberservices), 3) memberservices = \ self.dao.get_memberservices_by_subject(TEST_USER_ID, 'science') self.assertEqual(len(memberservices), 3) def test_get_memberservices_by_grade(self): grade_10_services = \ [s for s in self.services.objectValues() if s.grade == 'grade-10'] for count in range(0,3): service = grade_10_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_memberservices_by_grade(TEST_USER_ID, 'grade-10') self.assertEqual(len(memberservices), 3) def test_get_memberservices_by_subject_and_grade(self): grade_10_maths_services = \ [s for s in self.services.objectValues() if s.grade == 'grade-10'] grade_10_maths_services = \ [s for s in grade_10_maths_services if 'maths' in s.subject] for count in range(0,3): service = grade_10_maths_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_memberservices_by_subject_and_grade(TEST_USER_ID, 'maths', 'grade-10') self.assertEqual(len(memberservices), 3) memberservices = \ self.dao.get_memberservices_by_subject_and_grade(TEST_USER_ID, 'science', 'grade-10') self.assertEqual(len(memberservices), 0) def test_get_active_memberservices(self): for count in range(0,3): service = self.services.objectValues()[count] ms_args = self.get_ms_args(service, TEST_USER_ID) self.dao.add_memberservice(**ms_args) self.session.flush() now = datetime.now().date() td = timedelta(1) yesterday = now - td for count in range(3,6): service = self.services.objectValues()[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() active_memberservices = self.dao.get_active_memberservices(TEST_USER_ID) self.assertEquals(len(active_memberservices), 3) for ms in active_memberservices: self.assertEquals(ms.expiry_date, now) def test_get_active_memberservices_by_subject(self): now = datetime.now().date() td = timedelta(1) yesterday = now - td services = \ [s for s in self.services.objectValues() if 'maths' in s.subject] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() services = \ [s for s in self.services.objectValues() if 'science' in s.subject] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_active_memberservices_by_subject(TEST_USER_ID, 'maths') self.assertEqual(len(memberservices), 3) for ms in memberservices: self.assertEquals(ms.expiry_date, now) def test_get_active_memberservices_by_grade(self): now = datetime.now().date() td = timedelta(1) yesterday = now - td services = \ [s for s in self.services.objectValues() if s.grade == 'grade-10'] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() services = \ [s for s in self.services.objectValues() if s.grade == 'grade-11'] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_active_memberservices_by_grade(TEST_USER_ID, 'grade-10') self.assertEqual(len(memberservices), 3) for ms in memberservices: self.assertEquals(ms.expiry_date, now) def test_get_active_memberservices_by_subject_and_grade(self): now = datetime.now().date() td = timedelta(1) yesterday = now - td grade_10_maths_services = \ [s for s in self.services.objectValues() if \ s.grade == 'grade-10' and 'maths' in s.subject] for count in range(0,3): service = grade_10_maths_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() grade_10_science_services = \ [s for s in self.services.objectValues() if \ s.grade == 'grade-10' and 'science' in s.subject] for count in range(0,3): service = grade_10_science_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_memberservices_by_subject_and_grade(TEST_USER_ID, 'maths', 'grade-10') self.assertEqual(len(memberservices), 3) for ms in memberservices: self.assertEquals(ms.expiry_date, now) def test_get_expired_memberservices(self): now = datetime.now().date() for count in range(0,3): service = self.services.objectValues()[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() td = timedelta(1) yesterday = now - td for count in range(3,6): service = self.services.objectValues()[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() expired_memberservices = self.dao.get_expired_memberservices(TEST_USER_ID) self.assertEquals(len(expired_memberservices), 3) for ms in expired_memberservices: self.assertEquals(ms.expiry_date, yesterday) def test_get_expired_memberservices_by_subject(self): now = datetime.now().date() td = timedelta(1) yesterday = now - td services = \ [s for s in self.services.objectValues() if 'maths' in s.subject] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() services = \ [s for s in self.services.objectValues() if 'science' in s.subject] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_expired_memberservices_by_subject(TEST_USER_ID, 'science') self.assertEqual(len(memberservices), 3) for ms in memberservices: self.assertEquals(ms.expiry_date, yesterday) def test_get_expired_memberservices_by_grade(self): now = datetime.now().date() td = timedelta(1) yesterday = now - td services = \ [s for s in self.services.objectValues() if s.grade == 'grade-10'] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() services = \ [s for s in self.services.objectValues() if s.grade == 'grade-11'] for count in range(0,3): service = services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_expired_memberservices_by_grade(TEST_USER_ID, 'grade-11') self.assertEqual(len(memberservices), 3) for ms in memberservices: self.assertEquals(ms.expiry_date, yesterday) def test_get_expired_memberservices_by_subject_and_grade(self): now = datetime.now().date() td = timedelta(1) yesterday = now - td grade_10_maths_services = \ [s for s in self.services.objectValues() if \ s.grade == 'grade-10' and 'maths' in s.subject] for count in range(0,3): service = grade_10_maths_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = now self.dao.add_memberservice(**ms_args) self.session.flush() grade_10_science_services = \ [s for s in self.services.objectValues() if \ s.grade == 'grade-10' and 'science' in s.subject] for count in range(0,3): service = grade_10_science_services[count] ms_args = self.get_ms_args(service, TEST_USER_ID) ms_args['expiry_date'] = yesterday self.dao.add_memberservice(**ms_args) self.session.flush() memberservices = \ self.dao.get_expired_memberservices_by_subject_and_grade( TEST_USER_ID, 'science', 'grade-10') self.assertEqual(len(memberservices), 3) for ms in memberservices: self.assertEquals(ms.expiry_date, yesterday) def test_get_memberservice_by_primary_key(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.dao.get_memberservice_by_primary_key(ms1.id) self.assertEquals(ms1_db, self.get_memberservice(ms1.id)) def test_add_memberservice(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.get_memberservice(ms1.id) self.failUnless(IMemberService.providedBy(ms1_db)) def test_adding_duplicate_memberservices(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) ms2 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.get_memberservice(ms1.id) ms2_db = self.get_memberservice(ms2.id) self.failUnless(IMemberService.providedBy(ms1_db)) def test_update_memberservice(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.get_memberservice(ms1.id) ms1_db.title = 'new title' self.dao.update_memberservice(ms1_db) self.session.flush() ms1_db = self.get_memberservice(ms1.id) self.assertEquals(ms1_db.title, 'new title') def test_delete_memberservice(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.get_memberservice(ms1.id) self.dao.delete_memberservice(ms1_db) ms1_db = self.get_memberservice(ms1.id) self.assertEquals(ms1_db, None) def test_related_service(self): service = self.services.objectValues()[0] ms_args = self.get_ms_args(service, TEST_USER_ID) ms1 = self.dao.add_memberservice(**ms_args) self.session.flush() ms1_db = self.get_memberservice(ms1.id) self.failUnless(self.dao.related_service(ms1_db), service) def test_fti(self): fti = queryUtility(IDexterityFTI, name='emas.app.memberservice') self.assertEquals(None, fti) def test_schema(self): fti = queryUtility(IDexterityFTI, name='emas.app.memberservice') schema = fti.lookupSchema() self.assertEquals(None, schema) def test_factory(self): fti = queryUtility(IDexterityFTI, name='emas.app.memberservice') factory = fti.factory self.assertEquals(factory, None) def get_memberservice(self, memberservice_id): memberservices = self.session.query(MemberService).filter_by( id = memberservice_id).all() return memberservices and memberservices[0] or None def create_services(self): subjects = ['maths', 'science'] grades = ['10', '11', '12'] for subject in subjects: for count, grade in grades: props = {'service_type' : 'subscription', 'grade' : grade, 'subject' : subject, 'price' : '111'} s_id = self.services.invokeFactory('emas.app.service', '%s-%s' % (subject, grade), **props) service = self.services._getOb(s_id) service.subject = props['subject'] def get_ms_args(self, service, memberid): ms_args = { 'memberid': memberid, 'title': '%s for %s' % (service.title, memberid), 'related_service_id': self.intids.getId(service), 'expiry_date': datetime.now(), } return ms_args
class Practice(BrowserView): """ Proxy for practice in Monassis """ NUM_DAYS = 30 implements(IPractice, IPublishTraverse) index = ViewPageTemplateFile('templates/practice.pt') def __init__(self, context, request): super(Practice, self).__init__(context, request) self.settings = queryUtility(IRegistry).forInterface(IEmasSettings) def __call__(self, *args, **kw): alsoProvides(self.request, IPracticeLayer) self.subject = \ get_subject_from_path('/'.join(self.context.getPhysicalPath())) self.memberservices = [] self.dao = MemberServicesDataAccess(self.context) self.practice_services = [] self.accessto = '' portal_state = self.context.restrictedTraverse('@@plone_portal_state') path = self.request.get_header('PATH_INFO') member = portal_state.member() sm = getSecurityManager() self.ismanager = sm.checkPermission( permissions.ManagePortal, self.context) or False # give managers access to everything if self.ismanager: self.accessto = ('maths-grade-10,maths-grade-11,maths-grade-12,' 'science-grade-10,science-grade-11,science-grade-12') elif member.getId(): self.memberservices, self.practice_services = \ self.get_services(self.context, member.getId(), self.subject) self.accessto = self.get_accessto(self.practice_services) else: self.accessto = '' self.show_no_access_message = False if portal_state.anonymous(): memberid = 'Anonymous' else: memberid = member.getId() log.info('X-Access-To for %s: %s' % (memberid, self.accessto)) urlparts = urlparse(self.settings.practiceurl) practiceserver = urlparts.netloc path = self.request.get_header('PATH_INFO', '') if path and len(path) > 0: startpos = path.find(self.__name__) # strip the view name from the path path = path[startpos+len(self.__name__):] headers = { "Accept-Encoding": "identity", "Host": self.request.HTTP_HOST, "Connection": "close", "Cookie": self.request.HTTP_COOKIE, "Referer": self.request.HTTP_REFERER, "User-Agent": self.request.HTTP_USER_AGENT, } if not portal_state.anonymous(): headers["Authorization"] = 'Basic ' + base64.b64encode(memberid) headers["X-Access-To"] = self.accessto # Forward GET and POST requests; complain for all other request types if self.request.method == 'GET': conn = httplib.HTTPConnection(practiceserver) conn.request("GET", path, headers=headers) elif self.request.method == 'POST': headers['Content-Type'] = 'application/x-www-form-urlencoded' conn = httplib.HTTPConnection(practiceserver, timeout=30) conn.request("POST", path, body=urlencode(self.request.form.items()), headers=headers) else: return self.request.RESPONSE.unauthorized() self.html = '' # Handle response from Monassis server response = conn.getresponse() # Force no caching of response, unless /static or /image if tuple(path.split('/')[:2]) not in [('','static'), ('','image')]: self.request.RESPONSE.appendHeader('Cache-Control', 'no-store, no-cache') else: for key in ['Cache-Control', 'Expires']: value = response.getheader(key) if value is not None: self.request.RESPONSE.appendHeader(key, value) if response.status == 200: # Ok body = response.read() if response.msg.type == 'text/html': html = lxml.html.fromstring(body) html.make_links_absolute(base_url=self.settings.practiceurl, resolve_base_href=True) content = html.find('.//*[@id="content"]') if content is not None: self.html = lxml.html.tostring(content) else: self.html = body return self.index() else: resp = self.request.RESPONSE for key in ['Content-Length', 'Content-Type', 'Content-Disposition']: value = response.msg.get(key) if value is not None: resp.setHeader(key, value) resp.write(body) elif response.status == 302: # Found urlparts = urlparse(response.msg.get('location')) redirto = '%s%s' % (self.context.absolute_url(), urlparts.path) if urlparts.fragment: redirto += '#%s' % urlparts.fragment return self.request.RESPONSE.redirect(redirto) elif response.status == 400: # Bad request raise BadRequest('The URL:%s is a bad request.' %path) elif response.status == 403: # Forbidden if portal_state.anonymous(): return self.request.RESPONSE.unauthorized() else: log.info('User:%s not allowed to access URL:%s.' % (memberid, path)) self.show_no_access_message = True self.add_noaccess_message() return self.index() elif response.status == 404: # NotFound raise NotFound('The URL:%s could not be found.' %path) else: log.warn('Upstream returned:%s for URL:%s. Status is not handled.' % (response.status, path)) def add_first_login_message(self, member): last_login_time = member.getProperty('last_login_time') login_time = member.getProperty('login_time') # if it last login and current login are within 2 seconds of eachother, # we consider this the 'first login' if login_time.micros() - last_login_time.micros() < 2000000: plone_utils = getToolByName(self.context, 'plone_utils') message = _(u'Have a look at the practice services.') plone_utils.addPortalMessage(message, 'info') def services_active(self): """ If the user has the ManagePortal permission. OR If the user has any active services. We want to display the practice service content. """ return self.ismanager or len(self.memberservices) > 0 def practice_service_messages(self): grades = [10, 11, 12] messages = [] expiring_services = self.expiring_services() # Format messages about expiring services. if expiring_services.values(): msg = '' num_days = self.number_of_days_until(expiring_services) if num_days < 1: days = 'today.' else: days = num_days > 1 and 'in %s days.' or 'in %s day.' days = days % num_days template = 'Your access to %s practice will expire '+days service_grades = expiring_services.keys() service_grades.sort() if service_grades == grades: msg = template % self.subject.capitalize() else: services = ' and '.join(['Grade %s' %s for s in service_grades]) msg = template % services messages.append(msg) else: # no services expiring? Then don't show any messages. return [] # Now, we do the formatting of the active services. active_services = self.active_services() # Remove all the services that we already reported on above. for grade in expiring_services.keys(): active_services.pop(grade, None) if active_services.values(): # flatten the list of memberservice lists memberservices = ListType(chain.from_iterable(active_services.values())) # sort according to expiry_date memberservices.sort(key=lambda service: service.expiry_date) # use the first expiry date formatted_expiry_date = \ self.format_date(memberservices[0].expiry_date) msg = '' template = 'You will still have access to %s practice until %s.' service_grades = active_services.keys() service_grades.sort() services = ' and '.join(['Grade %s' %s for s in service_grades]) msg = template % (services, formatted_expiry_date) messages.append(msg) # Lastly, add a link to the order form. if expiring_services: messages.append( '<a href="/order">To extend your subscription, click here.</a>') return messages def format_date(self, expiry_date): if expiry_date.year == datetime.now().year: return expiry_date.strftime("%e %B") return expiry_date.strftime("%e %B %Y") def number_of_days_until(self, expiring_services): # flatten the list of memberservice lists memberservices = ListType(chain.from_iterable(expiring_services.values())) # sort according to expiry_date memberservices.sort(key=lambda service: service.expiry_date) # use the last expiry date expiry_date = memberservices[0].expiry_date now = datetime.now().date() delta = expiry_date - now return delta.days def expiring_services(self): now = datetime.now() expiring_services = {} for ms in self.memberservices: if self.is_expiring(now, ms): grade = int(self.dao.related_service(ms).grade.split('-')[-1]) tmpservices = expiring_services.get(grade, []) tmpservices.append(ms) expiring_services[grade] = tmpservices return expiring_services def active_services(self): now = datetime.now() active_services = {} for ms in self.memberservices: if not self.is_expiring(now, ms): grade = int(self.dao.related_service(ms).grade.split('-')[-1]) tmpservices = active_services.get(grade, []) tmpservices.append(ms) active_services[grade] = tmpservices return active_services def is_expiring(self, cutoff_date, memberservice): days = self.memberservice_expiry_threshold(memberservice) expiry_threshold = (cutoff_date + timedelta(days)).date() if memberservice.expiry_date <= expiry_threshold: return True return False def memberservice_expiry_threshold(self, memberservice): """ This method helps us decide when to show expiry warnings. For all services that have subscription_period of a YEAR or less, but not less than a MONTH, we want to show the message within the, 'annual_expiry_warning_threshold'. For all services that have subscription_period of a MONTH or less, we want to show the message within the, 'monthly_expiry_warning_threshold'. """ subperiod = self.dao.related_service(memberservice).subscription_period if subperiod <= MONTH: return self.settings.monthly_expiry_warning_threshold elif subperiod <= YEAR: return self.settings.annual_expiry_warning_threshold def get_services(self, context, userid, subject): memberservices = [] services = [] pps = self.context.restrictedTraverse('@@plone_portal_state') memberid = pps.member().getId() tmpservices = self.dao.get_active_memberservices_by_subject(memberid, subject) for ms in tmpservices: service = self.dao.related_service(ms) if service.access_path and '@@practice' in service.access_path: memberservices.append(ms) services.append(service) return memberservices, services def get_accessto(self, practice_services): accessto = ','.join( ['%s-%s' %(s.subject, s.grade) for s in practice_services] ) return accessto def add_noaccess_message(self): # set a portal message plone_utils = getToolByName(self.context, 'plone_utils') message = _(u'You do not currently have access to this service.') plone_utils.addPortalMessage(message, 'info') return message def publishTraverse(self, request, name): """ consume the subpath """ path = request['TraversalRequestNameStack'] subpath = path[:] path[:] = [] subpath.reverse() request.set('traverse_subpath', subpath) return self