def test_utcnow(): assert sedate.utcnow().replace( tzinfo=None, microsecond=0, second=0, minute=0) \ == datetime.utcnow().replace(microsecond=0, second=0, minute=0) assert sedate.utcnow().tzinfo == UTC
def test_notices_pdf_from_notices(gazette_app): session = gazette_app.session() with freeze_time("2017-01-01 12:00"): notice = GazetteNotice(title='first title', text='first text', author_place='first place', author_date=utcnow(), author_name='first author', state='submitted') notice.files.append(pdf_attachment('first attachment')) session.add(notice) session.flush() with freeze_time("2017-01-02 12:00"): notice = GazetteNotice(title='second title', text='second text', author_place='second place', author_date=utcnow(), author_name='second author', state='submitted') session.add(notice) session.flush() with freeze_time("2018-01-01 12:00"): request = DummyRequest(session, gazette_app.principal) notices = GazetteNoticeCollection(session) file = NoticesPdf.from_notices(notices, request) reader = PdfFileReader(file) assert [page.extractText() for page in reader.pages ] == [('© 2018 Govikon\n1\n' 'xxx\nfirst title\nfirst text\n' 'first place, 1. Januar 2017\nfirst author\n'), '© 2018 Govikon\n2\n', ('© 2018 Govikon\n3\n' 'xxx\nsecond title\nsecond text\n' 'second place, 2. Januar 2017\nsecond author\n')] file = NoticesPdf.from_notices(notices.for_order('title', 'desc'), request) reader = PdfFileReader(file) assert [page.extractText() for page in reader.pages ] == [('© 2018 Govikon\n1\n' 'xxx\nsecond title\nsecond text\n' 'second place, 2. Januar 2017\nsecond author\n' 'xxx\nfirst title\nfirst text\n' 'first place, 1. Januar 2017\nfirst author\n'), '© 2018 Govikon\n2\n']
def test_issues_pdf_notice(gazette_app): session = gazette_app.session() with freeze_time("2017-01-01 12:00"): notice = GazetteNotice(title='title', text='text', author_place='place', author_date=utcnow(), author_name='author', state='drafted') notice.files.append(pdf_attachment('attachment')) session.add(notice) session.flush() layout = Layout(notice, DummyRequest(session, gazette_app.principal)) pdf = IssuePdf(BytesIO()) pdf.init_a4_portrait() pdf.notice(notice, layout, '1') assert extract_pdf_story(pdf) == [ '1', 'title', 'text', 'place, 1. Januar 2017<br/>author' ] notice.print_only = True pdf.notice(notice, layout, '2') assert extract_pdf_story(pdf) == [ '1', 'title', 'text', 'place, 1. Januar 2017<br/>author', '2', '<i>This official notice is only available in the print version.</i>' ]
def has_permission_scan_job(app, identity, model, permission): # Editors and members of groups may view and edit scan jobs within # the same group if identity.role in ('editor', 'member'): if identity.groupid: if permission in {ViewModel, EditModel}: if same_group(model, identity): return True if permission is DeleteModel and identity.role == 'editor': if same_group(model, identity): dt = model.dispatch_date # editors of the same group may delete scan jobs up until # 17:00 on the day before the dispatch horizon = datetime(dt.year, dt.month, dt.day, 17) horizon -= timedelta(days=1) horizon = sedate.replace_timezone(horizon, 'Europe/Zurich') now = sedate.utcnow() return now <= horizon return permission in getattr(app.settings.roles, identity.role)
def remove_reservation_from_session(self, session_id, token): """ Removes the reservation with the given session_id and token. """ assert token and session_id query = self.reservations_by_session(session_id) query = query.filter(Reservation.token == token) reservation = query.one() self.session.delete(reservation) # if we get here the token must be valid, we should then check if the # token is used in the reserved slots, because with autoapproval these # slots may be created straight away. slots = self.session.query(ReservedSlot).filter( ReservedSlot.reservation_token == token ) slots.delete('fetch') # we also update the timestamp of existing reservations within # the same session to ensure that we account for the user's activity # properly during the session expiration cronjob. Otherwise it is # possible that a user removes the latest reservations only to see # the rest of them vanish because his older reservations were # already old enough to be counted as expired. query = self.session.query(Reservation) query = query.filter(Reservation.session_id == session_id) query.update({"modified": sedate.utcnow()})
def test_signature_timestamp(session): path = module_path('onegov.file', 'tests/fixtures/sample.pdf') time = sedate.utcnow() with open(path, 'rb') as f: session.add(File(name='sample.pdf', reference=f, signature_metadata={ 'timestamp': time.isoformat() })) transaction.commit() # if unsinged, the timestamp is ignored assert session.query(File).one().signature_timestamp is None assert session.query(File).with_entities(File.signature_timestamp).one()\ .signature_timestamp is None # if signed the timestamp is in UTC (not timezone-aware) session.query(File).one().signed = True transaction.commit() assert session.query(File).one().signature_timestamp == time assert session.query(File).with_entities(File.signature_timestamp).one()\ .signature_timestamp == time # make sure we can filter by time assert session.query(File).filter_by(signature_timestamp=time).first() # make sure we get utc for both assert session.query(File).one()\ .signature_timestamp.tzinfo.zone == 'UTC' assert session.query(File).with_entities(File.signature_timestamp).one()\ .signature_timestamp.tzinfo.zone == 'UTC'
def overdue_issues(self): """ Returns True, if any of the issue's deadline is reached. """ if self._issues: query = object_session(self).query(Issue) query = query.filter(Issue.name.in_(self._issues.keys())) query = query.filter(Issue.deadline < utcnow()) if query.first(): return True return False
def handle_send_notification(self, request, form): period = PeriodCollection(request.session).active() variables = TemplateVariables(request, period) layout = NotificationTemplateLayout(self, request) if form.submitted(request): recipients = form.recipients if not recipients: request.alert(_("There are no recipients matching the selection")) else: current = request.current_username if current not in recipients: recipients.add(current) subject = variables.render(self.subject) content = render_template('mail_notification.pt', request, { 'layout': DefaultMailLayout(self, request), 'title': subject, 'notification': variables.render(self.text) }) plaintext = html_to_text(content) for recipient in recipients: request.app.send_marketing_email( receivers=(recipient, ), subject=subject, content=content, plaintext=plaintext, ) self.last_sent = utcnow() request.success(_( "Successfully sent the e-mail to ${count} recipients", mapping={ 'count': len(recipients) } )) return request.redirect( request.class_link(NotificationTemplateCollection)) return { 'title': _("Mailing"), 'layout': layout, 'form': form, 'preview_subject': variables.render(self.subject), 'preview_body': variables.render(self.text), 'edit_link': request.return_here(request.link(self, 'edit')), 'button_text': _("Send E-Mail Now") }
def view_dashboard(self, request): """ The dashboard view (for editors). Shows the drafted, submitted and rejected notices, shows warnings and allows to create a new notice. """ layout = Layout(self, request) user_ids, group_ids = get_user_and_group(request) collection = GazetteNoticeCollection(request.session, user_ids=user_ids, group_ids=group_ids) # rejected rejected = collection.for_state('rejected').query().all() if rejected: request.message(_("You have rejected messages."), 'warning') # drafted drafted = collection.for_state('drafted').query().all() now = utcnow() limit = now + timedelta(days=2) past_issues_selected = False deadline_reached_soon = False for notice in drafted: for issue in notice.issue_objects: if issue.deadline < now: past_issues_selected = True elif issue.deadline < limit: deadline_reached_soon = True if past_issues_selected: request.message(_("You have drafted messages with past issues."), 'warning') if deadline_reached_soon: request.message( _("You have drafted messages with issues close to the deadline."), 'warning') # submitted submitted = collection.for_state('submitted').query().all() new_notice = request.link(collection.for_state('drafted'), name='new-notice') return { 'layout': layout, 'title': _("Dashboard"), 'rejected': rejected, 'drafted': drafted, 'submitted': submitted, 'new_notice': new_notice, 'current_issue': layout.current_issue }
def test_sign_file(app): tape = module_path('onegov.file', 'tests/cassettes/ais-success.json') with vcr.use_cassette(tape, record_mode='none'): ensure_correct_depot(app) transaction.begin() path = module_path('onegov.file', 'tests/fixtures/sample.pdf') with open(path, 'rb') as f: app.session().add(File(name='sample.pdf', reference=f)) with open(path, 'rb') as f: old_digest = hashlib.sha256(f.read()).hexdigest() transaction.commit() pdf = app.session().query(File).one() token = 'ccccccbcgujhingjrdejhgfnuetrgigvejhhgbkugded' with patch.object(Yubico, 'verify') as verify: verify.return_value = True app.sign_file(file=pdf, signee='*****@*****.**', token=token) transaction.commit() pdf = app.session().query(File).one() assert pdf.signed assert pdf.reference['content_type'] == 'application/pdf' assert pdf.signature_metadata['signee'] == '*****@*****.**' assert pdf.signature_metadata['old_digest'] == old_digest assert pdf.signature_metadata['new_digest'] assert pdf.signature_metadata['token'] == token assert pdf.signature_metadata['token_type'] == 'yubikey' assert pdf.signature_metadata['request_id']\ .startswith('swisscom_ais/foo/') assert len(pdf.reference.file.read()) > 0 timestamp = isodate.parse_datetime( pdf.signature_metadata['timestamp']) now = sedate.utcnow() assert (now - timedelta(seconds=10)) <= timestamp <= now with pytest.raises(RuntimeError) as e: app.sign_file(pdf, signee='*****@*****.**', token=token) assert "already been signed" in str(e)
def view_latest_event(self, request): """ Redirects to the latest occurrence of an event that is, either the next future event or the last event in the past if there are no more future events. """ now = utcnow() for occurrence in self.occurrences: if now < occurrence.start: return morepath.redirect(request.link(occurrence)) return morepath.redirect(request.link(occurrence))
def sections(self): now = sedate.utcnow() sections = (self.__class__({ 'Id': r['TeilbaustelleId'], 'Teilbaustellen': [], **r }) for r in self['Teilbaustellen']) sections = (s for s in sections if s['DauerVon']) sections = (s for s in sections if s['DauerVon'] <= now) sections = (s for s in sections if now <= (s['DauerBis'] or now)) return list(sections)
def publish_files(self, horizon=None): """ Publishes unpublished files with a publish date older than the given horizon. """ # default to a horizon slightly into the future as this method is # usually called by cronjob which is not perfectly on time horizon = horizon or (utcnow() + timedelta(seconds=90)) for f in self.publishable_files(horizon): f.published = True f.publish_date = None self.session.flush()
def on_request(self): session = self.request.session # populate organization (active root elements with no children or # active children (but not their parents)) self.organization.choices = [] self.organization.choices.append( ('', self.request.translate(_("Select one")))) query = session.query(Organization) query = query.filter(Organization.active.is_(True)) query = query.filter(Organization.parent_id.is_(None)) query = query.order_by(Organization.order) for root in query: if root.children: for child in root.children: if child.active: self.organization.choices.append( (child.name, child.title)) else: self.organization.choices.append((root.name, root.title)) # populate categories query = session.query(Category.name, Category.title) query = query.filter(Category.active.is_(True)) query = query.order_by(Category.order) self.category.choices = query.all() # populate issues now = utcnow() layout = Layout(None, self.request) self.issues.choices = [] query = session.query(Issue) query = query.order_by(Issue.date) if self.request.is_private(self.model): query = query.filter(date.today() < Issue.date) # publisher else: query = query.filter(now < Issue.deadline) # editor for issue in query: self.issues.choices.append( (issue.name, layout.format_issue(issue, date_format='date_with_weekday'))) if now >= issue.deadline: self.issues.render_kw['data-hot-issue'] = issue.name # Remove the print only option if not publisher if not self.request.is_private(self.model): self.delete_field('print_only')
def events(self, request): session = request.session stmt = self.attendee_calendar records = session.execute( select(stmt.c).where( and_(stmt.c.attendee_id == self.attendee_id, stmt.c.state == 'accepted', stmt.c.confirmed == True))) datestamp = utcnow() for record in records: event = icalendar.Event() event.add('uid', record.uid) event.add('summary', record.title) if record.note: event.add('description', record.note) event.add('dtstart', standardize_date(record.start, 'UTC')) event.add('dtend', standardize_date(record.end, 'UTC')) event.add('dtstamp', datestamp) event.add( 'url', request.class_link(VacationActivity, {'name': record.name})) if record.meeting_point: event.add('location', record.meeting_point) if record.lat and record.lon: event.add('geo', (float(record.lat), float(record.lon))) if record.meeting_point and record.lat and record.lon: event.add("X-APPLE-STRUCTURED-LOCATION", f"geo:{record.lat},{record.lon}", parameters={ "VALUE": "URI", "X-ADDRESS": record.meeting_point, "X-APPLE-RADIUS": "50", "X-TITLE": record.meeting_point }) yield event
def find_expired_reservation_sessions(self, expiration_date): """ Goes through all reservations and returns the session ids of the unconfirmed ones which are older than the given expiration date. By default the expiration date is now - 15 minutes. Note that this method goes through ALL RESERVATIONS OF THE CURRENT SESSION. This is NOT limited to a specific context or scheduler. """ expiration_date = expiration_date or ( sedate.utcnow() - timedelta(minutes=15) ) # first get the session ids which are expired query = self.session.query( Reservation.session_id, func.max(Reservation.created), func.max(Reservation.modified) ) query = query.group_by(Reservation.session_id) # != null() because != None is not allowed by PEP8 query = query.filter(Reservation.session_id != null()) # only pending reservations are considered query = query.filter(Reservation.status == 'pending') # the idea is to remove all reservations belonging to sessions whose # latest update is expired - either delete the whole session or let # all of it be expired_sessions = [] for session_id, created, modified in query.all(): modified = modified or created assert created and modified if max(created, modified) < expiration_date: expired_sessions.append(session_id) return expired_sessions
def propose_activity(self, request): assert request.app.active_period, "An active period is required" # if the latest request has been done in the last minute, this is a # duplicate and should be ignored latest = self.latest_request if latest and (sedate.utcnow() - timedelta(seconds=60)) < latest.created: return session = request.session with session.no_autoflush: self.propose() publication_request = self.create_publication_request( request.app.active_period) ticket = TicketCollection(session).open_ticket( handler_code='FER', handler_id=publication_request.id.hex ) TicketMessage.create(ticket, request, 'opened') send_ticket_mail( request=request, template='mail_ticket_opened.pt', subject=_("Your ticket has been opened"), receivers=(self.username, ), ticket=ticket, force=( request.is_organiser_only or request.current_username != self.username ) ) request.success(_("Thank you for your proposal!")) @request.after def redirect_intercooler(response): response.headers.add('X-IC-Redirect', request.link(ticket, 'status')) # do not redirect here, intercooler doesn't deal well with that... return
def test_notices_pdf_from_notice(gazette_app): session = gazette_app.session() with freeze_time("2017-01-01 12:00"): notice = GazetteNotice(title='title', text='text', author_place='place', author_date=utcnow(), author_name='author', state='drafted') notice.files.append(pdf_attachment('attachment')) session.add(notice) session.flush() with freeze_time("2018-01-01 12:00"): request = DummyRequest(session, gazette_app.principal) file = NoticesPdf.from_notice(notice, request) reader = PdfFileReader(file) assert [page.extractText() for page in reader.pages] == [ '© 2018 Govikon\n1\n' 'xxx\ntitle\ntext\nplace, 1. Januar 2017\nauthor\n', '© 2018 Govikon\n2\n' ]
def timestamp(): return sedate.utcnow()
def current_issue(self): return self.query().filter(Issue.deadline > utcnow()).first()
def expired(self): """ Returns True, if the notice is expired. """ if self.expiry_date: return self.expiry_date < utcnow() return False
def sign_file(self, file, signee, token, token_type='yubikey'): """ Signs the given file and stores metadata about that process. During signing the stored file is replaced with the signed version. For example:: pdf = app.sign_file(pdf, '*****@*****.**', 'foo') :param file: The :class:`onegov.file..File` instance to sign. :param signee: The name of the signee (should be a username). :param token: The (yubikey) token used to sign the file. WARNING: It is the job of the caller to ensure that the yubikey has the right to sign the document (i.e. that it is the right yubikey). :param token_type: They type of the passed token. Currently only 'yubikey'. """ if file.signed: raise AlreadySignedError(file) if token_type == 'yubikey': def is_valid_token(token): if not getattr(self, 'yubikey_client_id', None): raise TokenConfigurationError(token_type) return is_valid_yubikey( self.yubikey_client_id, self.yubikey_secret_key, expected_yubikey_id=yubikey_public_id(token), yubikey=token ) else: raise NotImplementedError(f"Unknown token type: {token_type}") if not is_valid_token(token): raise InvalidTokenError(token) mb = 1024 ** 2 session = object_session(file) with SpooledTemporaryFile(max_size=16 * mb, mode='wb') as signed: old_digest = digest(file.reference.file) request_id = self.signing_service.sign(file.reference.file, signed) new_digest = digest(signed) signed.seek(0) file.reference = FileIntent( fileobj=signed, filename=file.name, content_type=file.reference['content_type']) file.signature_metadata = { 'old_digest': old_digest, 'new_digest': new_digest, 'signee': signee, 'timestamp': utcnow().isoformat(), 'request_id': request_id, 'token': token, 'token_type': token_type } file.signed = True from onegov.file.models.file_message import FileMessage # circular FileMessage.log_signature(file, signee) session.flush()
def test_process_time(session): user = User() with freeze_time('2016-06-21') as frozen: # the created timestamp would usually be set as the session is flushed ticket = Ticket(state='open', created=Ticket.timestamp()) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is 0 assert ticket.last_state_change is None frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is 10 assert ticket.last_state_change is None ticket.accept_ticket(user) assert ticket.reaction_time == 10 assert ticket.process_time is None assert ticket.current_process_time == 0 assert ticket.last_state_change == utcnow() frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time == 10 assert ticket.process_time is None assert ticket.current_process_time == 10 assert ticket.last_state_change == utcnow() - timedelta(seconds=10) ticket.close_ticket() assert ticket.reaction_time == 10 assert ticket.process_time == 10 assert ticket.current_process_time == 10 assert ticket.last_state_change == utcnow() frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time == 10 assert ticket.process_time == 10 assert ticket.current_process_time == 10 assert ticket.last_state_change == utcnow() - timedelta(seconds=10) ticket.reopen_ticket(user) assert ticket.reaction_time == 10 assert ticket.process_time == 10 assert ticket.current_process_time == 10 assert ticket.last_state_change == utcnow() frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time == 10 assert ticket.process_time == 10 assert ticket.current_process_time == 20 assert ticket.last_state_change == utcnow() - timedelta(seconds=10) ticket.close_ticket() assert ticket.reaction_time == 10 assert ticket.process_time == 20 assert ticket.current_process_time == 20 assert ticket.last_state_change == utcnow()
def test_legacy_process_time(session): """ Tests the process_time/response_time for existing tickets, which cannot be migrated as this information cannot be inferred. """ user = User() # test if the changes work for existing pending tickets (we don't need # to check the open tickets, as there is no difference between an open # ticket before/after the lead time introduction) with freeze_time('2016-06-21') as frozen: ticket = Ticket(state='pending', created=Ticket.timestamp(), user=user) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change is None ticket.close_ticket() assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() - timedelta(seconds=10) ticket.reopen_ticket(user) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() - timedelta(seconds=10) ticket.close_ticket() assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() # test if the changes work for existing closed tickets with freeze_time('2016-06-21') as frozen: ticket = Ticket(state='closed', created=Ticket.timestamp()) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change is None ticket.reopen_ticket(user) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() frozen.tick(delta=timedelta(seconds=10)) assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow() - timedelta(seconds=10) ticket.close_ticket() assert ticket.reaction_time is None assert ticket.process_time is None assert ticket.current_process_time is None assert ticket.last_state_change == utcnow()