def parse_fullcalendar_request(request, timezone): """ Parses start and end from the given fullcalendar request. It is expected that no timezone is passed (the default). See `<http://fullcalendar.io/docs/timezone/timezone/>`_ :returns: A tuple of timezone-aware datetime objects or (None, None). """ start = request.params.get('start') end = request.params.get('end') if start and end: if 'T' in start: start = parse_datetime(start) end = parse_datetime(end) else: start = datetime.combine(parse_date(start), time(0, 0)) end = datetime.combine(parse_date(end), time(23, 59, 59, 999999)) start = sedate.replace_timezone(start, timezone) end = sedate.replace_timezone(end, timezone) return start, end else: return None, None
def add(self, title, start, end, timezone, autoclean=True, **optional): """ Add a new event. A unique, URL-friendly name is created automatically for this event using the title and optionally numbers for duplicate names. Every time a new event is added, old, stale events are deleted unless disabled with the ``autoclean`` option. Returns the created event or None if already automatically deleted. """ event = Event( state='initiated', title=title, start=replace_timezone(start, timezone), end=replace_timezone(end, timezone), timezone=timezone, name=self._get_unique_name(title), **optional ) self.session.add(event) self.session.flush() if autoclean: self.remove_stale_events() return event
def filter_by_year(self, query): timezone = 'Europe/Zurich' start = sedate.replace_timezone(datetime(self.year, 1, 1), timezone) end = sedate.replace_timezone(datetime(self.year + 1, 1, 1), timezone) return query.filter(and_( start <= MissionReport.date, MissionReport.date < end ))
def test_get_date_range(): assert sedate.get_date_range( sedate.replace_timezone(datetime(2015, 1, 1), 'Europe/Zurich'), datetime(2015, 1, 1, 12, 0).time(), datetime(2015, 1, 2, 11, 0).time(), ) == ( sedate.replace_timezone(datetime(2015, 1, 1, 12, 0), 'Europe/Zurich'), sedate.replace_timezone(datetime(2015, 1, 2, 11, 0), 'Europe/Zurich'), )
def update_model(self, model): """ Stores the form values on the page. """ # clear the recurrence to avoid updating all occurrences too much model.recurrence = '' model.title = self.title.data model.content = { 'description': self.description.data } model.location = self.location.data model.tags = self.tags.data model.timezone = 'Europe/Zurich' model.start = replace_timezone( datetime( self.start_date.data.year, self.start_date.data.month, self.start_date.data.day, self.start_time.data.hour, self.start_time.data.minute ), model.timezone ) end_date = self.start_date.data if self.end_time.data <= self.start_time.data: end_date += timedelta(days=1) model.end = replace_timezone( datetime( end_date.year, end_date.month, end_date.day, self.end_time.data.hour, self.end_time.data.minute ), model.timezone ) if self.weekly.data and self.end_date.data: until_date = to_timezone( replace_timezone( datetime( self.end_date.data.year, self.end_date.data.month, self.end_date.data.day, self.end_time.data.hour, self.end_time.data.minute ), model.timezone ), 'UTC' ) model.recurrence = ( "RRULE:FREQ=WEEKLY;WKST=MO;BYDAY={0};UNTIL={1}".format( ','.join(self.weekly.data), until_date.strftime('%Y%m%dT%H%M%SZ') ) ) model.meta = model.meta or {} model.meta['submitter_email'] = self.email.data
def test_align_range_to_day(): assert sedate.align_range_to_day( start=sedate.replace_timezone(datetime(2015, 1, 1, 10, 0), 'Europe/Zurich'), end=sedate.replace_timezone(datetime(2015, 1, 1, 11, 0), 'Europe/Zurich'), timezone='Europe/Zurich') == (sedate.replace_timezone( datetime(2015, 1, 1), 'Europe/Zurich'), sedate.replace_timezone( datetime(2015, 1, 1, 23, 59, 59, 999999), 'Europe/Zurich'))
def view_news(self, request): layout = NewsLayout(self, request) years = self.years try: year = int(request.params["year"]) except (ValueError, KeyError): year = years and years[0] or None query = self.news_query if year: start = replace_timezone(datetime(year, 1, 1), "UTC") query = query.filter(Page.created >= start) query = query.filter(Page.created < start.replace(year=year + 1)) if request.is_logged_in: layout.editbar_links = list(self.get_editbar_links(request)) children = query.all() else: children = request.exclude_invisible(query.all()) return { "layout": layout, "title": self.title, "name": self.trait_messages[self.trait]["name"], "page": self, "children": children, "years": years, "current_year": year, }
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 test_standardize_aware_date(): aware_date = sedate.replace_timezone(datetime(2014, 10, 1, 13, 30), 'Europe/Zurich') normalized = sedate.standardize_date(aware_date, 'Europe/Zurich') assert normalized.tzname() == 'UTC' assert normalized.replace(tzinfo=None) == datetime(2014, 10, 1, 11, 30)
def set_start(self, start): if not start.tzinfo: assert self.timezone start = sedate.replace_timezone(start, self.timezone) if self.raster is not None: self._start = rasterize_start(start, self.raster) else: self._start = rasterize_start(start, MIN_RASTER)
def set_end(self, end): if not end.tzinfo: assert self.timezone end = sedate.replace_timezone(end, self.timezone) if self.raster is not None: self._end = rasterize_end(end, self.raster) else: self._end = rasterize_end(end, MIN_RASTER)
def test_align_date_to_day_up(): unaligned = sedate.standardize_date(datetime(2012, 1, 24, 10), 'UTC') aligned = sedate.align_date_to_day(unaligned, 'Europe/Zurich', 'up') assert aligned.tzname() == 'UTC' assert aligned == sedate.standardize_date( datetime(2012, 1, 24, 23, 59, 59, 999999), 'Europe/Zurich') already_aligned = sedate.replace_timezone( datetime(2012, 1, 1, 23, 59, 59, 999999), 'Europe/Zurich') assert already_aligned == sedate.align_date_to_day(already_aligned, 'Europe/Zurich', 'up')
def test_is_whole_day(): assert sedate.is_whole_day( sedate.replace_timezone( datetime(2015, 6, 30), 'Europe/Zurich'), sedate.replace_timezone( datetime(2015, 7, 1), 'Europe/Zurich'), 'Europe/Zurich' ) assert sedate.is_whole_day( sedate.replace_timezone( datetime(2015, 6, 30), 'Europe/Zurich'), sedate.replace_timezone( datetime(2015, 6, 30, 23, 59, 59), 'Europe/Zurich'), 'Europe/Zurich' ) assert not sedate.is_whole_day( sedate.replace_timezone( datetime(2015, 6, 30), 'Europe/Zurich'), sedate.replace_timezone( datetime(2015, 6, 30, 1), 'Europe/Zurich'), 'Europe/Zurich' ) assert not sedate.is_whole_day( sedate.replace_timezone( datetime(2015, 6, 30), 'Europe/Zurich'), sedate.replace_timezone( datetime(2015, 6, 30, 23, 59, 58), 'Europe/Zurich'), 'Europe/Zurich' ) assert not sedate.is_whole_day( sedate.replace_timezone( datetime(2015, 6, 30, 0, 0, 0, 999), 'Europe/Zurich'), sedate.replace_timezone( datetime(2015, 6, 30, 23, 59, 59), 'Europe/Zurich'), 'Europe/Zurich' )
def extract_date(mission): d = datetime.utcfromtimestamp(int(mission.date)) d = sedate.replace_timezone(d, 'Europe/Zurich') time = mission.alert_time.replace('.', ':') time = time.replace(';', ':') time = time.replace('::', ':') if ':' in time: h, m = (int(v.strip('<>-')) for v in time.split(':')) else: h = int(time) m = 0 d = d.replace(hour=h) d = d.replace(minute=m) return d
def view_get_occurrence(self, request): """ View a single occurrence of an event. """ layout = OccurrenceLayout(self, request) today = replace_timezone(as_datetime(date.today()), self.timezone) occurrences = self.event.occurrence_dates(localize=True) occurrences = list(filter(lambda x: x >= today, occurrences)) description = linkify(self.event.description).replace('\n', '<br>') session = request.app.session() ticket = TicketCollection(session).by_handler_id(self.event.id.hex) return { 'description': description, 'layout': layout, 'occurrence': self, 'occurrences': occurrences, 'ticket': ticket, 'title': self.title, }
def signature_timestamp(self): if self.signed: return sedate.replace_timezone( isodate.parse_datetime(self.signature_metadata['timestamp']), 'UTC' )
def query(self, start=None, end=None, tags=None, outdated=False): """ Queries occurrences with the given parameters. Finds all occurrences with any of the given tags and within the given start and end date. Start and end date are assumed to be dates only and therefore without a timezone - we search for the given date in the timezone of the occurrence!. If no start date is given and ``outdated`` is not set, only current occurrences are returned. """ query = self.session.query(Occurrence) if start is not None: assert type(start) is date start = as_datetime(start) expressions = [] for timezone in self.used_timezones: localized_start = replace_timezone(start, timezone) localized_start = standardize_date(localized_start, timezone) expressions.append( and_( Occurrence.timezone == timezone, Occurrence.start >= localized_start ) ) query = query.filter(or_(*expressions)) elif not outdated: start = as_datetime(date.today()) expressions = [] for timezone in self.used_timezones: localized_start = replace_timezone(start, timezone) localized_start = standardize_date(localized_start, timezone) expressions.append( and_( Occurrence.timezone == timezone, Occurrence.start >= localized_start ) ) query = query.filter(or_(*expressions)) if end is not None: assert type(end) is date end = as_datetime(end) end = end + timedelta(days=1) expressions = [] for timezone in self.used_timezones: localized_end = replace_timezone(end, timezone) localized_end = standardize_date(localized_end, timezone) expressions.append( and_( Occurrence.timezone == timezone, Occurrence.end < localized_end ) ) query = query.filter(or_(*expressions)) if tags: query = query.filter(Occurrence._tags.has_any(array(tags))) query = query.order_by(Occurrence.start, Occurrence.title) return query
def test_editor_delete_day_before(wtfs_app, wtfs_password): session = wtfs_app.session() def permits(user, model, permission): return permits_by_app(wtfs_app, user, model, permission) # Remove existing users and group session.query(User).delete() session.query(Municipality).delete() # Add two towns foo = uuid4() session.add(Municipality( id=foo, name='Foo', bfs_number=1, )) session.add(PickupDate( date=date.today(), municipality_id=foo, )) bar = uuid4() session.add(Municipality( id=bar, name='Bar', bfs_number=1, )) session.add(PickupDate( date=date.today(), municipality_id=bar, )) # add a single scan job to foo session.add(ScanJob( type='normal', municipality_id=foo, delivery_number=1, dispatch_date=date(2019, 1, 1)) ) # an admin with access to all of it session.add(User( username='******', password_hash=wtfs_password, role='admin' )) # an editor with access to foo session.add(User( username='******', password_hash=wtfs_password, role='editor', group_id=foo )) # a member with access to foo session.add(User( username='******', password_hash=wtfs_password, role='member', group_id=foo )) # an editor with access to bar session.add(User( username='******', password_hash=wtfs_password, role='editor', group_id=bar )) # a member with access to bar session.add(User( username='******', password_hash=wtfs_password, role='member', group_id=bar )) session.flush() def fetch_user(username): return session.query(User).filter_by(username=username).one() job = session.query(ScanJob).one() admin = fetch_user('*****@*****.**') foo_editor = fetch_user('*****@*****.**') foo_member = fetch_user('*****@*****.**') bar_editor = fetch_user('*****@*****.**') bar_member = fetch_user('*****@*****.**') dt = sedate.replace_timezone(datetime(2018, 12, 31), 'Europe/Zurich') with freeze_time(dt.replace(hour=17, minute=0)): assert permits(admin, job, DeleteModel) assert permits(foo_editor, job, DeleteModel) assert not permits(foo_member, job, DeleteModel) assert not permits(bar_editor, job, DeleteModel) assert not permits(bar_member, job, DeleteModel) with freeze_time(dt.replace(hour=17, minute=1)): assert permits(admin, job, DeleteModel) assert not permits(foo_editor, job, DeleteModel) assert not permits(foo_member, job, DeleteModel) assert not permits(bar_editor, job, DeleteModel) assert not permits(bar_member, job, DeleteModel)
def parse_datetime(dt): d = datetime.utcfromtimestamp(int(dt)) d = sedate.replace_timezone(d, 'UTC') d = sedate.to_timezone(d, 'Europe/Zurich') return d
def send_daily_ticket_statistics(request): today = replace_timezone(datetime.utcnow(), 'UTC') today = to_timezone(today, 'Europe/Zurich') if today.weekday() in (SAT, SUN): return args = {} # get the current ticket count collection = TicketCollection(request.app.session()) count = collection.get_count() args['currently_open'] = count.open args['currently_pending'] = count.pending args['currently_closed'] = count.closed # get tickets created yesterday or on the weekend end = datetime(today.year, today.month, today.day, tzinfo=today.tzinfo) if today.weekday() == MON: start = end - timedelta(days=2) else: start = end - timedelta(days=1) query = collection.query() query = query.filter(Ticket.created >= start) query = query.filter(Ticket.created <= end) args['opened'] = query.count() query = collection.query() query = query.filter(Ticket.modified >= start) query = query.filter(Ticket.modified <= end) query = query.filter(Ticket.state == 'pending') args['pending'] = query.count() query = collection.query() query = query.filter(Ticket.modified >= start) query = query.filter(Ticket.modified <= end) query = query.filter(Ticket.state == 'closed') args['closed'] = query.count() # render the email args['title'] = request.translate( _("${town} OneGov Cloud Status", mapping={ 'town': request.app.town.name }) ) args['layout'] = DefaultMailLayout(object(), request) args['is_monday'] = today.weekday() == MON args['town'] = request.app.town.name content = render_template('mail_daily_ticket_statistics.pt', request, args) # send one e-mail per user users = UserCollection(request.app.session()).query() users = users.options(undefer('data')) users = users.all() for user in users: if user.data and not user.data.get('daily_ticket_statistics'): continue request.app.send_email( subject=args['title'], receivers=(user.username, ), content=content )
def tzdatetime(year, month, day, hour, minute, timezone): return replace_timezone(datetime(year, month, day, hour, minute), timezone)