def has_activity_permission(owner, user, role, state): return has_private_permission_activities(app=None, identity=Bunch(userid=user, role=role), model=Bunch(state=state, username=owner), permission=Private)
def has_permission(owner, user, role, state): return has_public_permission_logged_in(app=None, identity=Bunch(userid=user, role=role), model=Bunch(state=state, username=owner), permission=Public)
def test_translate_sponsor(): sponsor = Sponsor( name="Evilcorp", logo={ 'de': 'das-logo.png', 'fr': 'le-logo.png' }, banners={'bookings': { 'url': { 'de': 'die-url', 'fr': 'le-url' } }}) de = sponsor.compiled(Bunch(locale='de_CH')) assert de.name == "Evilcorp" assert de.background is None assert de.logo == 'das-logo.png' assert de.banners == {'bookings': {'url': 'die-url'}} fr = sponsor.compiled(Bunch(locale='fr_CH')) assert fr.name == "Evilcorp" assert fr.background is None assert fr.logo == 'le-logo.png' assert fr.banners == {'bookings': {'url': 'le-url'}}
def test_vacation_activity_form(session, test_password): users = UserCollection(session) users.add(username='******', realname='Robert Baratheon', password='******', role='admin') users.add(username='******', realname=None, password='******', role='editor') users.add(username='******', realname=None, password='******', role='member') form = VacationActivityForm() form.request = Bunch(is_admin=True, current_username='******', session=session) form.on_request() assert form.username.data == '*****@*****.**' assert form.username.choices == [('*****@*****.**', '*****@*****.**'), ('*****@*****.**', 'Robert Baratheon')] form.request.is_admin = False form.on_request() assert form.username is None
def request(admin): return Bunch(app=Bunch( active_period=periods.active(), org=Bunch(geo_provider='geo-mapbox'), invoice_collection=invoice_collection, periods=periods.query().all(), ), session=session, include=lambda *args: None, model=None, is_admin=admin, is_organiser_only=not admin and True or False, is_manager=admin and True or False, translate=lambda text, *args, **kwargs: text, locale='de_CH', current_username=(admin and '*****@*****.**' or '*****@*****.**'))
def load_files(path): prefix = 'tx_winscan_domain_model' files = Bunch() files.users = as_csv(path / 'User_Scanauftrag_neu.xlsx') files.bill = as_csv(path / f'{prefix}_bill.csv') files.date = as_csv(path / f'{prefix}_date.csv') files.paymenttype = as_csv(path / f'{prefix}_paymenttype.csv') files.township = as_csv(path / f'{prefix}_township.csv') files.transportorder = as_csv(path / f'{prefix}_transportorder.csv') files.transporttype = as_csv(path / f'{prefix}_transporttype.csv') return files
def test_town_create(onboarding_app, temporary_directory, smtp, redis_url): c = Client(onboarding_app) a = c.get('/for-towns/1') a.form['name'] = 'New York' a.form['user'] = '******' a.form['color'] = '#ff00ff' a = a.form.submit().follow() assert 'New York' in a assert '*****@*****.**' in a assert '#f0f' in a assert 'new-york.example.org' in a a = a.form.submit() assert 'https://new-york.example.org' in a assert '*****@*****.**' in a assert len(smtp.outbox) == 1 username = '******' password = a.pyquery('.product dd:nth-child(4)').text() scan_morepath_modules(onegov.town.TownApp) morepath.commit(onegov.town.TownApp) town = TownApp() town.namespace = onboarding_app.onboarding['onegov.town']['namespace'] town.configure_application( dsn=onboarding_app.dsn, filestorage='fs.osfs.OSFS', filestorage_options={ 'root_path': temporary_directory, 'create': True }, identity_secure=False, redis_url=redis_url, enable_elasticsearch=False, depot_backend='depot.io.memory.MemoryFileStorage') town.set_application_id(town.namespace + '/' + 'new_york') town.settings.cronjobs = Bunch(enabled=False) c = Client(town) p = c.get('/') assert "Willkommen bei der OneGov Cloud" in p assert "New York" in p p = c.get('/auth/login') p.forms[1]['username'] = username p.forms[1]['password'] = password p = p.forms[1].submit().follow() assert 'Benutzerprofil' in p
def parse_definition(definition): for service in yaml.safe_load(definition): service_id = normalize_for_url(service['titel']) days = (d.strip() for d in service['tage'].split(',')) yield service_id, Bunch( id=service_id, title=service['titel'], percentage=Decimal(service['prozent']), days=OrderedSet(SERVICE_DAYS[d.lower()[:2]] for d in days), )
def test_hint_field(wtfs_app): def get_translate(for_chameleon): return wtfs_app.chameleon_translations.get('de_CH') form = Form() field = HintField(macro='express_shipment_hint') field = field.bind(form, 'hint') field.meta.request = Bunch(app=wtfs_app, get_translate=get_translate) assert field.validate(form) assert "Für dringende Scan-Aufträge" in field()
def test_timestamp_sponsor(): sponsor = Sponsor(name="Evilcorp", logo={ 'de': '{timestamp}', 'fr': '{timestamp}' }, banners='{timestamp}') with freeze_time("2017-10-12 16:30"): de = sponsor.compiled(Bunch(locale='de_CH')) assert de.name == "Evilcorp" assert de.logo == '1507825800000' assert de.banners == '1507825800000'
def ensure_no_payment_changes_after_confirmation(self): if isinstance(self.model, Period) and self.model.confirmed: preview = Bunch() self.populate_obj(preview) fields = ('all_inclusive', 'booking_cost') for field in fields: if getattr(self.model, field) != getattr(preview, field): self.pass_system.errors.append( _("It is no longer possible to change the execution " "settings since the period has already been confirmed" )) return False
def bills(self): bills = OrderedDict() invoices = self.invoices_by_period for username, items in groupby(invoices, lambda i: i.username): items = tuple(items) first = items[0] bills[username] = Bunch( items=items, first=first, id=first.id, invoice_id=first.invoice_id, title=first.realname or first.username, paid=first.invoice_paid, total=first.invoice_amount, outstanding=first.invoice_outstanding, discourage_changes=first.invoice_changes == 'discouraged', disable_changes=first.invoice_changes == 'impossible', has_online_payments=first.has_online_payments) return bills
def create_app(app_class, request, use_elasticsearch=False, reuse_filestorage=True, use_smtp=True, depot_backend='depot.io.local.LocalFileStorage', depot_storage_path=None): # filestorage can be reused between tries as it is nowadays mainly (if not # exclusively) used by the theme compiler if reuse_filestorage: filestorage_object = request.getfixturevalue('long_lived_filestorage') else: filestorage_object = None if not app_class.is_committed(): scan_morepath_modules(app_class) app_class.commit() if use_elasticsearch: elasticsearch_hosts = [request.getfixturevalue('es_url')] else: elasticsearch_hosts = [] if depot_backend == 'depot.io.local.LocalFileStorage': if not depot_storage_path: depot_storage_path = request.getfixturevalue('temporary_directory') temporary_path = request.getfixturevalue('temporary_path') signing_services = (temporary_path / 'signing-services') signing_services.mkdir() cert_file = module_path('onegov_testing', 'tests/fixtures/test.crt') cert_key = module_path('onegov_testing', 'tests/fixtures/test.crt') with (signing_services / '__default__.yml').open('w') as f: f.write( textwrap.dedent(f""" name: swisscom_ais parameters: customer: foo key_static: bar cert_file: {cert_file} cert_key: {cert_key} """)) app = app_class() app.namespace = random_namespace() app.configure_application( dsn=request.getfixturevalue('postgres_dsn'), filestorage='fs.osfs.OSFS', filestorage_object=filestorage_object, depot_backend=depot_backend, depot_storage_path=depot_storage_path, identity_secure=False, enable_elasticsearch=use_elasticsearch, elasticsearch_hosts=elasticsearch_hosts, redis_url=request.getfixturevalue('redis_url'), yubikey_client_id='foo', yubikey_secret_key='dGhlIHdvcmxkIGlzIGNvbnRyb2xsZWQgYnkgbGl6YXJkcyE=', signing_services=str(signing_services)) app.set_application_id(app.namespace + '/test') app.clear_request_cache() if hasattr(app, 'bind_depot'): app.bind_depot() # cronjobs leave lingering sessions open, in real life this is not a # problem, but in testing it leads to connection pool exhaustion app.settings.cronjobs = Bunch(enabled=False) if use_smtp: smtp = request.getfixturevalue('smtp') app.mail = { 'marketing': { 'host': smtp.address[0], 'port': smtp.address[1], 'force_tls': False, 'username': None, 'password': None, 'use_directory': False, 'sender': '*****@*****.**' }, 'transactional': { 'host': smtp.address[0], 'port': smtp.address[1], 'force_tls': False, 'username': None, 'password': None, 'use_directory': False, 'sender': '*****@*****.**' } } app.smtp = smtp return app
def app(self): return Bunch(org=self)
def handle_import(request, app): context = Context(request.session) created = Bunch(towns={}, users=[], dates=[], jobs=[]) townids = {} deleted = set() for record in files.township: if record.deleted == '1': deleted.add(record.uid) continue # towns double as user groups group = context.groups.add( name=record.name, bfs_number=record.bfs_nr, address_supplement=record.address_extension, gpn_number=record.gp_nr.isdigit() and int(record.gp_nr), payment_type=town_payment_type(record), type='wtfs') townids[record.uid] = record.bfs_nr assert group.bfs_number not in created.towns created.towns[group.bfs_number] = group print(f"✓ Imported {len(created.towns)} towns") for record in files.users: user = User( username=record.email, role=roles[record.rolle], realname=record.name, active=True, second_factor=None, signup_token=None, password_hash=password_hash, group_id=record.bfs and created.towns[record.bfs].id or None, data={'contact': record.kontakt == 'j'}) context.session.add(user) created.users.append(user) print(f"✓ Imported {len(created.users)} users") for record in files.date: if record.township in deleted: continue if record.deleted == '1' or record.township == '0': continue dt = parse_datetime(record.date).date() pickup_date = PickupDate( date=dt, municipality_id=created.towns[townids[record.township]].id) context.session.add(pickup_date) created.dates.append(pickup_date) print(f"✓ Imported {len(created.dates)} dates") for record in files.transportorder: dispatch_date = parse_datetime(record.distribution_date).date() return_date = parse_datetime(record.return_date).date() if record.deleted == '1' or record.township == '0': continue if record.township in deleted: continue job = ScanJob( municipality_id=created.towns[townids[record.township]].id, type=types[record.transport_type], delivery_number=record.delivery_bill_number, # dispatch (in) dispatch_date=dispatch_date, dispatch_note=record.comment_delivery, dispatch_boxes=int(record.box_in), dispatch_tax_forms_current_year=int( record.tax_current_year_in), dispatch_tax_forms_last_year=int( record.tax_last_year_in), dispatch_tax_forms_older=int( record.tax_old_in), dispatch_single_documents=int( record.single_voucher_in), # targets (ribbon stands for "Bändliweg" I think) dispatch_cantonal_tax_office=int( record.ribbon_out), dispatch_cantonal_scan_center=int( record.cantonal_scan_center), # return (out) return_date=return_date, return_note=record.comment_handover, return_boxes=record.box_out, return_tax_forms_current_year=int( record.tax_current_year_out), return_tax_forms_last_year=int( record.tax_last_year_out), return_tax_forms_older=int( record.tax_old_out), return_single_documents=int( record.single_voucher_out), return_unscanned_tax_forms_current_year=int( record.not_scanned_current_year), return_unscanned_tax_forms_last_year=int( record.not_scanned_last_year), return_unscanned_tax_forms_older=int( record.not_scanned_old), return_unscanned_single_documents=int( record.not_scanned_voucher) ) context.session.add(job) created.jobs.append(job) print(f"✓ Imported {len(created.jobs)} jobs")
def calculate_precisely(self, daycare, services, income, wealth, rebate): """ Creates a detailed calculation of the subsidy paid by Winterthur. The reslt is a list of tables with explanations. :param daycare: The selected daycare (a :class:`Daycare` instance). :param services: Services used (a :class:`Services` instance) :param income: The income as a decimal. :param wealth: The wealth as decimal. :param rebate: True if a rebate is applied Note, due to the specific nature of the content here, which is probably not going to be translated, we use German. For consistency we want to limit this, but with Winterthur these kinds of things crop up as the wording is quite specific and adding translations would just make this a lot harder. """ cfg = self.settings fmt = format_precise # Base Rate # --------- base = Block('base', "Berechnungsgrundlage für die Elternbeiträge") base.op(title="Steuerbares Einkommen", amount=income, note=""" Steuerbares Einkommen gemäss letzter Veranlagung. """) base.op(title="Vermögenszuschlag", amount=max((wealth - cfg.max_wealth) * cfg.wealth_premium / Decimal('100'), 0), operation="+", note=f""" Der Vermögenszuschlag beträgt {fmt(cfg.wealth_premium).rstrip('.0')}% des Vermögens, für das tatsächlich Steuern anfallen (ab {fmt(cfg.max_wealth)} CHF). """) base.op(title="Massgebendes Gesamteinkommen", operation="=") base.op(title="Abzüglich Minimaleinkommen", operation="-", amount=cfg.min_income) base.op(title="Berechnungsgrundlage", operation="=") # Gross Contribution # ------------------ gross = Block('gross', "Berechnung des Brutto-Elternbeitrags") gross.op(title="Übertrag", amount=base.total) gross.op(title="Faktor", amount=cfg.factor(daycare), currency=None, operation="×", note=""" Ihr Elternbeitrag wird aufgrund eines Faktors berechnet (Kita-Reglement Art. 20 Abs 3). """, output_format=format_precise, amount_places=10) gross.op(title="Einkommensabhängiger Elternbeitragsbestandteil", operation="=") gross.op(title="Mindestbeitrag Eltern", amount=cfg.min_rate, operation="+") gross.op(title="Elternbeitrag brutto", operation="=", amount=min(gross.total, daycare.rate)) # Rebate # ------ rebate = gross.total * cfg.rebate / 100 if rebate else 0 net = Block('net', "Berechnung des Rabatts") net.op(title="Übertrag", amount=gross.total) net.op(title="Rabatt", amount=rebate, operation="-", note=f""" Bei einem Betreuungsumfang von insgesamt mehr als 2 ganzen Tagen pro Woche gilt ein Rabatt von {fmt(cfg.rebate).rstrip('0.')}%. """) net.op(title="Elternbeitrag netto", operation="=", amount=max(cfg.min_rate, gross.total - rebate)) # Actual contribution # ------------------- actual = Block('actual', ("Berechnung des Elternbeitrags und des " "städtischen Beitrags pro Tag")) actual.op(title="Übertrag", amount=net.total) actual.op(title="Zusatzbeitrag Eltern", amount=max(daycare.rate - cfg.max_rate, 0), operation="+", note=f""" Zusatzbeitrag für Kitas, deren Tagestarif über {cfg.max_rate} CHF liegt. """) parent_share_per_day = actual.op(title="Elternbeitrag pro Tag", operation="=", note=""" Ihr Beitrag pro Tag (100%) und Kind. """, important=True) city_share_per_day = actual.op(title="Städtischer Beitrag pro Tag", amount=max( daycare.rate - parent_share_per_day, Decimal('0.00')), important=True, note=""" Städtischer Beitrag für Ihr Kind pro Tag. """) # Monthly contribution # -------------------- monthly = Block('monthly', ("Berechnung des Elternbeitrags und des städtischen " "Beitrags pro Monat")) monthly.op(title="Wochentarif", amount=parent_share_per_day * services.total / 100, note=""" Wochentarif: Elternbeiträge der gewählten Betreuungstage. """) monthly.op(title="Faktor", amount=daycare.factor, currency=None, operation="×", note=""" Faktor für jährliche Öffnungswochen Ihrer Kita. """, output_format=format_precise, amount_places=4) parent_share_per_month = monthly.op(title="Elternbeitrag pro Monat", operation="=", important=True, output_format=format_5_cents) city_share_per_month = monthly.op( title="Städtischer Beitrag pro Monat", amount=city_share_per_day * services.total / 100 * daycare.factor, important=True, output_format=format_5_cents) # Services table # -------------- def services_table(): total = Decimal(0) total_percentage = Decimal(0) for day in SERVICE_DAYS.values(): for service_id in services.selected: if day in services.selected[service_id]: service = services.available[service_id] cost = parent_share_per_day * service.percentage / 100 total += cost total_percentage += service.percentage label = SERVICE_DAYS_LABELS[day] yield (label, service.title, format_5_cents(cost)) yield (_("Total"), None, format_5_cents(total)) total = round_to(parent_share_per_month, '0.05')\ + round_to(city_share_per_month, '0.05') return Bunch( blocks=(base, gross, net, actual, monthly), parent_share_per_month=format_5_cents(parent_share_per_month), city_share_per_month=format_5_cents(city_share_per_month), total_per_month=format_5_cents(total), agenda=tuple(services_table()), )
def test_notification_template_send_form(session): activities = ActivityCollection(session, type='vacation') attendees = AttendeeCollection(session) periods = PeriodCollection(session) occasions = OccasionCollection(session) bookings = BookingCollection(session) users = UserCollection(session) admin = users.add(username='******', realname='Robert Baratheon', password='******', role='admin') organiser = users.add(username='******', realname=None, password='******', role='editor') users.add(username='******', realname=None, password='******', role='member') prebooking = tuple(d.date() for d in (datetime.now() - timedelta(days=1), datetime.now() + timedelta(days=1))) execution = tuple(d.date() for d in (datetime.now() + timedelta(days=10), datetime.now() + timedelta(days=12))) period = periods.add(title="Ferienpass 2016", prebooking=prebooking, execution=execution, active=True) foo = activities.add("Foo", username='******') foo.propose().accept() bar = activities.add("Bar", username='******') bar.propose().accept() o1 = occasions.add( start=datetime(2016, 11, 25, 8), end=datetime(2016, 11, 25, 16), age=(0, 10), spots=(0, 2), timezone="Europe/Zurich", activity=foo, period=period, ) o1.username = admin.username o2 = occasions.add( start=datetime(2016, 11, 25, 17), end=datetime(2016, 11, 25, 20), age=(0, 10), spots=(0, 2), timezone="Europe/Zurich", activity=bar, period=period, ) o2.username = organiser.username a1 = attendees.add(admin, 'Dustin', date(2000, 1, 1), 'male') a2 = attendees.add(organiser, 'Mike', date(2000, 1, 1), 'female') b1 = bookings.add(admin, a1, o1) b1.state = 'accepted' b1.cost = 100 b2 = bookings.add(organiser, a2, o2) b2.state = 'accepted' b2.cost = 100 transaction.commit() # create a mock request def invoice_collection(user_id=None, period_id=None): return InvoiceCollection(session, user_id=user_id, period_id=period_id) def request(admin): return Bunch(app=Bunch( active_period=periods.active(), org=Bunch(geo_provider='geo-mapbox'), invoice_collection=invoice_collection, periods=periods.query().all(), ), session=session, include=lambda *args: None, model=None, is_admin=admin, is_organiser_only=not admin and True or False, is_manager=admin and True or False, translate=lambda text, *args, **kwargs: text, locale='de_CH', current_username=(admin and '*****@*****.**' or '*****@*****.**')) # in the beginning there are no recipients form = NotificationTemplateSendForm() form.model = None form.request = request(admin=True) assert form.has_choices # we still have choices (like send to users) assert not form.occasion.choices # once the request is processed, the occasions are added form.on_request() assert form.has_choices assert len(form.occasion.choices) == 2 assert len(form.send_to.choices) == 7 # if the period is inactive, there are no occasions periods.query().one().active = False transaction.commit() form = NotificationTemplateSendForm() form.model = None form.request = request(admin=True) form.on_request() assert len(form.occasion.choices) == 0 # if the period is active but not confirmed, there are no recipients period = periods.query().one() period.active = True period.confirmed = False transaction.commit() form = NotificationTemplateSendForm() form.model = None form.request = request(admin=True) form.on_request() occasions = [c[0] for c in form.occasion.choices] # with organisers assert len(form.recipients_by_occasion(occasions, True)) == 2 # without assert len(form.recipients_by_occasion(occasions, False)) == 0 # the number of users is indepenedent of the period assert len(form.recipients_by_role(('admin', 'editor', 'member'))) == 3 assert len(form.recipients_by_role(('admin', 'editor'))) == 2 assert len(form.recipients_by_role(('admin', ))) == 1 # if the period is confirmed, there are accepted recipients period = periods.query().one() period.active = True period.confirmed = True transaction.commit() assert len(form.recipients_by_occasion(occasions)) == 2 # only accepted bookings are counted parent = admin.username bookings.query().filter_by(username=parent).one().state = 'cancelled' transaction.commit() # without organisers assert len(form.recipients_by_occasion(occasions, False)) == 1 # with assert len(form.recipients_by_occasion(occasions, True)) == 2 # inactive users may be exluded form.state.data = ['active'] assert len(form.recipients_pool) == 3 form.state.data = ['active', 'inactive'] assert len(form.recipients_pool) == 3 form.state.data = ['inactive'] assert len(form.recipients_pool) == 0 # bookings count towards the wishlist if the period is active, period = periods.query().one() period.active = True period.confirmed = False transaction.commit() form.request = request(admin=True) # do not count cancelled bookings... assert len(form.recipients_with_wishes()) == 2 assert len(form.recipients_with_bookings()) == 0 # otherwise they count towards the bookings period = periods.query().one() period.confirmed = True transaction.commit() form.request = request(admin=True) assert len(form.recipients_with_wishes()) == 0 assert len(form.recipients_with_bookings()) == 2 # count the active organisers form.request = request(admin=True) assert len(form.recipients_which_are_active_organisers()) == 2 # count the users with unpaid bills form.request = request(admin=True) assert len(form.recipients_with_unpaid_bills()) == 0 period = periods.query().one() billing = BillingCollection(request=Bunch( session=session, app=Bunch(invoice_collection=invoice_collection)), period=period) billing.create_invoices() transaction.commit() form.request = request(admin=True) assert len(form.recipients_with_unpaid_bills()) == 1 # organisers are not counted as active if the occasion has been cancelled occasions = OccasionCollection(session) occasions.query().first().cancelled = True transaction.commit() form.request = request(admin=True) assert len(form.recipients_which_are_active_organisers()) == 1 for occasion in occasions.query(): occasion.cancelled = False transaction.commit() form.request = request(admin=True) assert len(form.recipients_which_are_active_organisers()) == 2
def has_permission(state): return has_public_permission_not_logged_in(app=None, identity=None, model=Bunch(state=state), permission=Public)
def test_is_owner(): assert not is_owner(username=None, activity=Bunch(username=None)) assert is_owner(username='******', activity=Bunch(username='******'))