def setUp(self): super().setUp() QuarterFactory(period=DateRange(date(2018, 1, 1), date(2018, 2, 1))) self.section = SectionFactory()
def groups( cache_name=None, throttle=None, no_commit=False, ): from mptracker.scraper.groups import GroupScraper, Interval http_session = create_session(cache_name=cache_name, throttle=throttle and float(throttle)) group_scraper = GroupScraper(http_session) mandate_lookup = models.MandateLookup() mandate_intervals = defaultdict(list) groups = list(group_scraper.fetch()) independents = groups[0] assert independents.is_independent for group in groups[1:] + [independents]: for member in group.current_members + group.former_members: (year, chamber, number) = member.mp_ident assert chamber == 2 mandate = mandate_lookup.find(member.mp_name, year, number) interval_list = mandate_intervals[mandate] interval = member.get_interval() if interval.start is None: interval = interval._replace(start=TERM_2012_START) if group.is_independent: if interval_list: start = interval_list[-1].end interval = interval._replace(start=start) interval_list.append(interval) interval_list.sort(key=lambda i: i[0]) for mandate, interval_list in mandate_intervals.items(): # make sure interval_list are continuous new_intervals = [] for interval_one, interval_two in \ zip(interval_list[:-1], interval_list[1:]): assert interval_one.start < interval_one.end if interval_one.end < interval_two.start: interval = Interval( start=interval_one.end, end=interval_two.start, group=independents, ) new_intervals.append(interval) elif interval_one.end > interval_two.start: raise RuntimeError("Overlapping intervals") interval_list.extend(new_intervals) interval_list.sort() mandate_end = mandate.interval.upper if mandate_end == date.max: mandate_end = None if interval_list[-1].end != mandate_end: logger.warn("Mandate %s ends at %s", mandate, interval_list[-1].end) group_patcher = TablePatcher( models.MpGroup, models.db.session, key_columns=['short_name'], ) with group_patcher.process(remove=True) as add_group: for group in groups: record = group.as_dict(['name', 'short_name']) group.row = add_group(record).row models.db.session.flush() membership_patcher = TablePatcher( models.MpGroupMembership, models.db.session, key_columns=['mandate_id', 'mp_group_id', 'interval'], ) with membership_patcher.process( autoflush=1000, remove=True, ) as add_membership: for mandate, interval_list in mandate_intervals.items(): for interval in interval_list: row = add_membership({ 'mandate_id': mandate.id, 'mp_group_id': interval.group.row.id, 'interval': DateRange( interval.start or date.min, interval.end or date.max, ), }).row if no_commit: logger.warn("Rolling back the transaction") models.db.session.rollback() else: models.db.session.commit()
def test_date_open(self): field = pg_forms.DateRangeField() value = field.clean(['', '2013-04-09']) self.assertEqual(value, DateRange(None, datetime.date(2013, 4, 9)))
def setUp(self): Season.objects.create(duration=DateRange( date(year=2016, month=10, day=1), date(year=2017, month=3, day=31)))
def save(self, *args, **kwargs): ignore_overlap = kwargs.pop('ignore_overlap', False) self.clean(ignore_overlap=ignore_overlap) self.duration = DateRange(self.start, self.end, '[]') return super(Period, self).save(*args, **kwargs)
def get_calendar(self, operating_profile, operating_period): calendar_dates = [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), operation=False) for date_range in operating_profile.nonoperation_days ] calendar_dates += [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), special=True, operation=True) for date_range in operating_profile.operation_days ] dates = [] for holiday in operating_profile.operation_bank_holidays: if holiday in BANK_HOLIDAYS: for date in BANK_HOLIDAYS[holiday]: if operating_period.contains(date): if date not in dates: dates.append(date) calendar_dates.append( CalendarDate(start_date=date, end_date=date, dates=DateRange(date, date, '[]'), special=True, operation=True, summary=holiday)) else: self.undefined_holidays.add(holiday) dates = [] for holiday in operating_profile.nonoperation_bank_holidays: if holiday in BANK_HOLIDAYS: for date in BANK_HOLIDAYS[holiday]: if operating_period.contains(date): if date not in dates: dates.append(date) calendar_dates.append( CalendarDate(start_date=date, end_date=date, dates=DateRange(date, date, '[]'), operation=False, summary=holiday)) else: self.undefined_holidays.add(holiday) sodt = operating_profile.serviced_organisation_day_type summary = [] non_operation_days = [] operation_days = [] if sodt: if sodt.non_operation_working_days is sodt.non_operation_holidays: pass elif sodt.non_operation_working_days: if sodt.non_operation_working_days.name: summary.append( f'not {sodt.non_operation_working_days.name} days') non_operation_days += sodt.non_operation_working_days.working_days elif sodt.non_operation_holidays: if sodt.non_operation_holidays.name: summary.append( f'not {sodt.non_operation_holidays.name} holidays') non_operation_days += sodt.non_operation_holidays.holidays calendar_dates += [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), operation=False) for date_range in non_operation_days ] if sodt.operation_working_days is sodt.operation_holidays: pass elif sodt.operation_working_days: if sodt.operation_working_days.name: summary.append(f'{sodt.operation_working_days.name} days') operation_days += sodt.operation_working_days.working_days elif sodt.operation_holidays: if sodt.operation_holidays.name: summary.append(f'{sodt.operation_holidays.name} holidays') operation_days += sodt.operation_holidays.holidays calendar_dates += [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), operation=True) for date_range in operation_days ] summary = ', '.join(summary) if operating_period.start == operating_period.end: if summary: summary = f"{summary}, " summary = f"{summary}{operating_period.start.strftime('%A %-d %B %Y')} only" if not calendar_dates and not operating_profile.regular_days and not summary: return calendar_hash = f'{operating_profile.regular_days}{operating_period.dates()}{summary}' calendar_hash += ''.join(f'{date.dates}{date.operation}{date.special}' for date in calendar_dates) if calendar_hash in self.calendar_cache: return self.calendar_cache[calendar_hash] if summary: summary = get_summary(summary) calendar = Calendar(mon=False, tue=False, wed=False, thu=False, fri=False, sat=False, sun=False, start_date=operating_period.start, end_date=operating_period.end, dates=operating_period.dates(), summary=summary) for day in operating_profile.regular_days: if day == 0: calendar.mon = True elif day == 1: calendar.tue = True elif day == 2: calendar.wed = True elif day == 3: calendar.thu = True elif day == 4: calendar.fri = True elif day == 5: calendar.sat = True elif day == 6: calendar.sun = True calendar.save() for date in calendar_dates: date.calendar = calendar CalendarDate.objects.bulk_create(calendar_dates) self.calendar_cache[calendar_hash] = calendar return calendar
def test_normalise_hypothesis_daterange(self, a): a = DateRange(*a) cursor = connection.cursor() cursor.execute("SELECT %s::daterange", [a]) self.assertEqual(cursor.fetchone()[0], normalise(a), a)
def subscribe(self, request, *args, **kwargs): # Make a select for update lock on admin to make sure subscriptions are properly saved admin = Admin.objects.select_for_update().get(pk=request.user.id) plan = self.object = self.get_object() serializer = self.get_serializer(data=request.data) if serializer.is_valid(): now = timezone.now() commitment = serializer.validated_data['commitment'] try: current_subscription = Subscription.objects.select_related( 'plan').active_for_admin(admin_id=admin.id, now=now).get() is_paid_plan = current_subscription.plan.paid_plan except Subscription.DoesNotExist: current_subscription = None is_paid_plan = False start_date = now # Depending if current plan is paid one or not, there is a different logic on subscribing if is_paid_plan: # If current plan is a paid one, next subscription need to start on next billing cycle. start_date += timedelta( hours=settings.BILLING_GRACE_PERIOD_FOR_PLAN_CHANGING) start_date += relativedelta(day=1, months=+1) start_date = start_date.date() charged_until = start_date # If there is any - delete a subscription that's yet to start (it wasn't charged) Subscription.objects.filter( admin_id=admin.id, range__startswith=start_date).delete() # Check if last subscription is the same one as the one we want to subscribe to last_subscription = Subscription.objects.filter( admin=admin).last() if last_subscription.plan_id == plan.id and last_subscription.commitment == commitment: last_subscription.range = DateRange( last_subscription.start, None) last_subscription.save() return Response( SubscriptionSerializer( last_subscription, context=self.get_serializer_context()).data) else: # If current plan is a free one, start paid plan asap start_date = start_date.date() charged_until = start_date + relativedelta(day=1, months=+1) # Invalidate cached subscription as we are about to create a new one for current period Profile.invalidate_active_subscription(admin.id) # Calculate plan fee plan_fee = plan.get_plan_fee(commitment, start_date=start_date) # Charge invoice invoice = Invoice( admin=admin, plan_fee=plan_fee, period=start_date.replace(day=1), due_date=start_date + timedelta(days=settings.BILLING_DEFAULT_DUE_DATE), is_prorated=start_date.day != 1) invoice.save() charge_result = invoice.charge() InvoiceItem.objects.create(invoice=invoice, source=InvoiceItem.SOURCES.PLAN_FEE, quantity=1, price=plan_fee) # If charge failed, reverse everything done here, no paid plan for you! if charge_result is not True: raise PaymentFailed(str(charge_result)) # Finish current subscription if current_subscription: current_subscription.range = DateRange( current_subscription.start, start_date) current_subscription.save() # Finally create subscription subscription = Subscription.objects.create( range=DateRange(start_date, None), commitment=commitment, admin=admin, charged_until=charged_until, plan=plan) return Response( SubscriptionSerializer( subscription, context=self.get_serializer_context()).data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# -*- coding: utf-8 -*- # Generated by Django 1.11.4 on 2017-10-29 00:02 from __future__ import unicode_literals from datetime import date from django.db import migrations from psycopg2.extras import DateRange DEFAULT_RUN_DATE = DateRange(date(2017, 1, 1), date(2017, 1, 4), '[)') def provide_default_date_range(apps, schema_editor): ContentPlacement = apps.get_model('budget', 'ContentPlacement') for placement in ContentPlacement.objects.filter(run_date__isnull=True): placement.run_date = DEFAULT_RUN_DATE placement.save() class Migration(migrations.Migration): dependencies = [ ('budget', '0036_auto_20171028_1655'), ] operations = [ migrations.RunPython(provide_default_date_range), ]
def daterange(faker, field, *args, **kwargs): lower = faker.date_time().date() upper = faker.date_time_between_dates(datetime_start=lower).date() return DateRange(lower, upper)
def get_queryset(self): qs = super().get_queryset() qs = qs.select_related('plan').filter(admin=self.request.user) # Filter out subscriptions that have already ended so we always get the current one on top of the list return qs.exclude(range__fully_lt=DateRange(date.today()))
def compress(self, data_list): if data_list: start_date, stop_date = data_list return DateRange(start_date, stop_date)
def get_periods_for_range(self, start, end=None, include_drafts=False, include_deleted=False, period_type=None): """Returns the period, annotated with rotation metadata and prefetched opening hours, that determines the opening hours for each date in the range, or None Parameters: start (date): Starting date for requested date range end (date): Ending date for requested date range. If omitted, only return one date. include_drafts (bool): Whether non-published periods are taken into account, for preview purposes include_deleted (bool): Whether deleted periods are taken into account period_type (string): Only consider 'normal' or 'override' periods, or None (override overrides normal). Returns: OrderedDict[Period]: The period that determines opening hours for each date, with max_month and max_week metadata, and prefetched opening hours data. """ if not end: # default only returns single day end = start potential_periods = self.periods.filter( period__overlap=DateRange(lower=start, upper=end, bounds='[]')) if period_type: if period_type == 'normal': potential_periods.filter(override=False) if period_type == 'override': potential_periods.filter(override=True) if not include_drafts: potential_periods = potential_periods.filter(published=True) if not include_deleted: potential_periods = potential_periods.filter(deleted=False) # 1 query + join all openings and linked hours periods = [ p for p in potential_periods.prefetch_related( 'openings__daily_hours') ] # store the rotation metadata per period for period in periods: # Weekly rotation is subordinate to monthly rotation, if present. # max_month 0 means no monthly rotation (only weekly rotation) period.max_month = period.openings.aggregate( Max('month'))['month__max'] # max_week 0 means no weekly rotation (only monthly rotation) period.max_week = period.openings.aggregate( Max('week'))['week__max'] # shorter periods always take precedence periods.sort( key=lambda x: (-x.override, x.period.upper - x.period.lower)) dates = pd.date_range(start, end).date active_periods = OrderedDict() for date in dates: for period in periods: if date in period.period: active_periods[date] = period break else: active_periods[date] = None return active_periods
def _test_range_overlaps(self, constraint): # Create exclusion constraint. self.assertNotIn(constraint.name, self.get_constraints(HotelReservation._meta.db_table)) with connection.schema_editor() as editor: editor.add_constraint(HotelReservation, constraint) self.assertIn(constraint.name, self.get_constraints(HotelReservation._meta.db_table)) # Add initial reservations. room101 = Room.objects.create(number=101) room102 = Room.objects.create(number=102) datetimes = [ timezone.datetime(2018, 6, 20), timezone.datetime(2018, 6, 24), timezone.datetime(2018, 6, 26), timezone.datetime(2018, 6, 28), timezone.datetime(2018, 6, 29), ] reservation = HotelReservation.objects.create( datespan=DateRange(datetimes[0].date(), datetimes[1].date()), start=datetimes[0], end=datetimes[1], room=room102, ) constraint.validate(HotelReservation, reservation) HotelReservation.objects.create( datespan=DateRange(datetimes[1].date(), datetimes[3].date()), start=datetimes[1], end=datetimes[3], room=room102, ) HotelReservation.objects.create( datespan=DateRange(datetimes[3].date(), datetimes[4].date()), start=datetimes[3], end=datetimes[4], room=room102, cancelled=True, ) # Overlap dates. with self.assertRaises(IntegrityError), transaction.atomic(): reservation = HotelReservation( datespan=(datetimes[1].date(), datetimes[2].date()), start=datetimes[1], end=datetimes[2], room=room102, ) msg = f"Constraint “{constraint.name}” is violated." with self.assertRaisesMessage(ValidationError, msg): constraint.validate(HotelReservation, reservation) reservation.save() # Valid range. other_valid_reservations = [ # Other room. HotelReservation( datespan=(datetimes[1].date(), datetimes[2].date()), start=datetimes[1], end=datetimes[2], room=room101, ), # Cancelled reservation. HotelReservation( datespan=(datetimes[1].date(), datetimes[1].date()), start=datetimes[1], end=datetimes[2], room=room102, cancelled=True, ), # Other adjacent dates. HotelReservation( datespan=(datetimes[3].date(), datetimes[4].date()), start=datetimes[3], end=datetimes[4], room=room102, ), ] for reservation in other_valid_reservations: constraint.validate(HotelReservation, reservation) HotelReservation.objects.bulk_create(other_valid_reservations) # Excluded fields. constraint.validate( HotelReservation, HotelReservation( datespan=(datetimes[1].date(), datetimes[2].date()), start=datetimes[1], end=datetimes[2], room=room102, ), exclude={"room"}, ) constraint.validate( HotelReservation, HotelReservation( datespan=(datetimes[1].date(), datetimes[2].date()), start=datetimes[1], end=datetimes[2], room=room102, ), exclude={"datespan", "start", "end", "room"}, )
class TestTableTypes(TrackedTable): name = 'test_table_types' default_values = OrderedDict([ ('test_table_types_bool', True), ('test_table_types_real', 9.99), ('test_table_types_double', 9.99), ('test_table_types_smallint', 9), ('test_table_types_integer', 50000), ('test_table_types_bigint', 5000000000), ('test_table_types_numeric', Decimal('1.1')), ('test_table_types_varchar', 'hi there'), ('test_table_types_text', 'hi there'), ('test_table_types_bytea', b'hi there'), ('test_table_types_date', date(year=1991, month=11, day=11)), ('test_table_types_time', time(hour=11, minute=39, second=22)), ('test_table_types_timetz', time(hour=11, minute=39, second=22, tzinfo=pytz.utc)), ('test_table_types_timestamp', datetime(year=1991, month=11, day=11, hour=11, minute=39, second=22)), ('test_table_types_timestamptz', datetime(year=1991, month=11, day=11, hour=11, minute=39, second=22, tzinfo=pytz.utc)), ('test_table_types_interval', timedelta(hours=15)), ('test_table_types_array', ['a', 'b', 'c', 'd', 'e']), # ('test_table_types_hstore', {'a': 1, 'b': 2, 'c': 3}), this dict would be passed to test_table_types_hstore ('test_table_types_int4range', NumericRange(lower=1, upper=10)), ('test_table_types_int8range', NumericRange(lower=1, upper=50000)), ('test_table_types_numrange', NumericRange(lower=Decimal('0.1'), upper=Decimal('1.1'))), ('test_table_types_daterange', DateRange( lower=date(year=1991, month=11, day=11), upper=date(year=1991, month=11, day=21), )), ('test_table_types_tsrange', DateTimeRange( lower=datetime(year=1991, month=11, day=11, hour=11, minute=39, second=22), upper=datetime(year=1991, month=11, day=21, hour=11, minute=39, second=22), )), ('test_table_types_tstzrange', DateTimeTZRange( lower=datetime(year=1991, month=11, day=11, hour=11, minute=39, second=22, tzinfo=pytz.utc), upper=datetime(year=1991, month=11, day=21, hour=11, minute=39, second=22, tzinfo=pytz.utc), )), # ('test_table_types_uuid', UUID(bytes=b'1234567890123456')), # ('test_table_types_inet', ip_network('192.168.0.0')), # ('test_table_types_cidr', ip_network('192.168.0.0')), ('test_table_types_json', json.dumps(['a', {'b': 2}, 2])), ('test_table_types_jsonb', json.dumps(['a', {'b': 2}, 2])), ]) def build(self, cursor=None): if not cursor: cursor = self.get_cursor() cursor.execute( "CREATE TABLE test_table_types (" "test_table_types_bool bool DEFAULT %s, " "test_table_types_real real DEFAULT %s, " "test_table_types_double double precision DEFAULT %s, " "test_table_types_smallint smallint DEFAULT %s, " "test_table_types_integer integer DEFAULT %s, " "test_table_types_bigint bigint DEFAULT %s, " "test_table_types_numeric numeric DEFAULT %s, " "test_table_types_varchar varchar DEFAULT %s, " "test_table_types_text text DEFAULT %s, " "test_table_types_bytea bytea DEFAULT %s, " "test_table_types_date date DEFAULT %s, " "test_table_types_time time DEFAULT %s, " "test_table_types_timetz timetz DEFAULT %s, " "test_table_types_timestamp timestamp DEFAULT %s, " "test_table_types_timestamptz timestamptz DEFAULT %s, " "test_table_types_interval interval DEFAULT %s, " "test_table_types_array text[] DEFAULT %s, " # "test_table_types_hstore hstore DEFAULT %s, " hstore is unused. "test_table_types_int4range int4range DEFAULT %s, " "test_table_types_int8range int8range DEFAULT %s, " "test_table_types_numrange numrange DEFAULT %s, " "test_table_types_daterange daterange DEFAULT %s, " "test_table_types_tsrange tsrange DEFAULT %s, " "test_table_types_tstzrange tstzrange DEFAULT %s, " # "test_table_types_uuid uuid DEFAULT %s, " uuid is unused. # "test_table_types_inet inet DEFAULT %s, " inet types are unused. # "test_table_types_cidr cidr DEFAULT %s, " "test_table_types_json json DEFAULT %s, " "test_table_types_jsonb jsonb DEFAULT %s " ")", list(self.default_values.values()) ) for i in range(1000): cursor.execute("INSERT INTO test_table_types DEFAULT VALUES")
def date_range(in_row): d = datetime.datetime.strptime(in_row['date'], "%Y-%j") return DateRange(lower=d.date(), upper=d.date(), bounds='[]')
def buildGraphs(): featureGraph = nx.Graph() # There are some polygon sources that we want to ignore, so we identify the sources to excludeSourceRefs = list() sourceDict = {} sourceDict.update(API_META) sourceDict.update(CSV_META) sourceDict.update(GEOJSON_META) for sourceName in sourceDict: if not sourceDict[sourceName]['forConflict']: excludeSourceRefs.append(API_element.objects.get(api_name=sourceName).id) excludeStatuses = ['COMPLETED', 'COMPLETED', 'REQUESTED', 'COMPLETE', 'DENIED', 'CANCELED'] filteredFeatures = Feature.objects\ .exclude(canonical_daterange=None)\ .exclude(canonical_daterange__isempty=True)\ .exclude(neighborhood__isnull=True)\ .exclude(source_ref__in=excludeSourceRefs)\ .exclude(canonical_status__in=excludeStatuses)\ .filter(canonical_daterange__overlap=DateRange(lower='2015-01-01', upper='2020-01-01')) print('filteredFeatures count', filteredFeatures.count()) for idx, f1 in enumerate(filteredFeatures): print('idx', idx) for f2 in filteredFeatures[idx+1:]: if f1.id == f2.id: continue daysApart = getDayDiff(f1, f2) if daysApart < 60: featureGraph.add_nodes_from([f1.id, f2.id]) featureGraph.add_edge(f1.id, f2.id, {'daysApart':daysApart}) # if idx > 10: # break print('nodecount', nx.number_of_nodes(featureGraph)) print('edgecount', nx.number_of_edges(featureGraph)) datedFeatureIDs = [f for f in featureGraph.nodes()] datedFeatures = Feature.objects.filter(pk__in=datedFeatureIDs) # for e in featureGraph.edges(data=True): # print(e) counter = 0 # print(datedFeatures.get(pk=46907)) # print(featureGraph[46907]) # print(featureGraph.neighbors(46907)) for f1 in datedFeatures: connected = featureGraph.neighbors(f1.id) cids = [f for f in connected] connectedFeatures = Feature.objects.filter(pk__in=cids) counter += 1 print('counter: {} of {}'.format(counter, datedFeatures.count())) for f2 in connectedFeatures.annotate(distance=Distance('geom', f1.geom)): # print(f1.id, f2.id) # print(f1.canonical_daterange, f2.canonical_daterange) featureGraph.add_edge(f1.id, f2.id, distance=f2.distance.m) print('nodecount', nx.number_of_nodes(featureGraph)) print('edgecount', nx.number_of_edges(featureGraph)) cache.set('featureGraph', featureGraph, None)
def date_range(in_row): return DateRange(lower=in_row[lower], upper=in_row[upper])
def test_with_hypothesis_dates(self, a, b): a = DateRange(*a) b = DateRange(*b) cursor = connection.cursor() cursor.execute("SELECT %s::daterange && %s::daterange", [a, b]) self.assertEqual(cursor.fetchone()[0], a & b, "{} && {}".format(a, b))
def date_range(in_row): return DateRange(lower=in_row[columns[0]], upper=in_row[columns[0]], bounds='[]')
def test_may_not_compare_different_range_types(self): with self.assertRaises(TypeError): NumericRange() & DateRange()
import datetime from decimal import Decimal from psycopg2.extras import DateRange from sqlalchemy.sql.expression import and_, func from pygotham_2019.meta import session from pygotham_2019.model import DrugInfo, DrugPrice from pygotham_2019.utils import print_table if __name__ == "__main__": session.add_all([ DrugInfo( id="1234567890", validity=DateRange( lower="1970-01-01", upper="2019-10-04", ), name="Lipitor", description="20mg tablet", ), DrugInfo( id="1234567890", validity=DateRange( lower="2019-10-04", upper="9999-12-31", ), name="Lipitor", description="20mg Tablet", ), DrugInfo( id="9999999999",
def get_opening_hours(time_zone, periods, begin, end=None): """ Returns opening and closing times for a given date range Return value is a dict where keys are days on the range and values are a list of Day objects for that day's active period containing opening and closing hours :rtype : dict[str, list[dict[str, datetime.datetime]]] :type periods: list[Period] :type begin: datetime.date | datetime.datetime :type end: datetime.date | None """ tz = pytz.timezone(time_zone) if begin is not None: if isinstance(begin, datetime.datetime): begin = datetime_to_date(begin, tz) assert isinstance(begin, datetime.date) if end is not None: if isinstance(end, datetime.datetime): end = datetime_to_date(end, tz) assert isinstance(end, datetime.date) if begin is None: begin = tz.localize(datetime.datetime.now()).date() if end is None: end = begin assert begin <= end if begin == end: d_range = DateRange(begin, end, '[]') else: d_range = DateRange(begin, end) # Periods are taken into account the shortest first. periods = periods.filter(duration__overlap=d_range)\ .annotate(length=dbm.F('end')-dbm.F('start'))\ .order_by('length') days = Day.objects.filter(period__in=periods) periods = list(periods) for period in periods: period.range_days = { day.weekday: day for day in days if day.period == period } date = begin dates = {} while date <= end: opens = None closes = None for period in periods: if period.start > date or period.end < date: continue # Currently the 'closed' field of periods do not # always contain sensible data. Ignore it for now. if False and period.closed: break day = period.range_days.get(date.weekday()) if day is None or day.closed: break opens = combine_datetime(date, day.opens, tz) closes = combine_datetime(date, day.closes, tz) break dates[date] = [{'opens': opens, 'closes': closes}] date += datetime.timedelta(days=1) return dates
def get_opening_hours(begin, end, resources=None): """ :type begin:datetime.date :type end:datetime.date :type resources: Resource | None :rtype: dict[datetime, dict[Resource, list[OpenHours]]] Find opening hours for all resources on a given time period. If resources is None, finds opening hours for all resources. This version goes through all regular periods and then all exception periods that are found overlapping the given time range. It builds a dict of days that has dict of resources with their active hours. TODO: There is couple optimization avenues worth exploring with prefetch or select_related for Periods' relational fields Unit, Resource and Day. This way all relevant information could be requested with one or two queries from the db. """ if not resources: resources = Resource.objects.all() if not begin < end: end = begin + datetime.timedelta(days=1) d_range = DateRange(begin, end) periods = Period.objects.filter( djdbm.Q(resource__in=resources) | djdbm.Q(unit__in=resources.values("unit__pk")), duration__overlap=d_range).order_by('exception') begin_dt = datetime.datetime.combine(begin, datetime.time(0, 0)) end_dt = datetime.datetime.combine(end, datetime.time(0, 0)) # Generates a dict of time range's days as keys and values as active period's days # all requested dates are assumed closed dates = {r.date(): False for r in arrow.Arrow.range('day', begin_dt, end_dt)} for period in periods: if period.start < begin: start = begin_dt else: start = arrow.get(period.start) if period.end > end: end = end_dt else: end = arrow.get(period.end) if period.resource: period_resources = [period.resource] else: period_resources = period.unit.resources.filter(pk__in=resources) for res in period_resources: for r in arrow.Arrow.range('day', start, end): for day in period.days.all(): if day.weekday is r.weekday(): if not dates.get(r.date(), None): dates[r.date] = {} dates[r.date()].setdefault( res, []).append( OpenHours(day.opens, day.closes)) return dates
def people( year='2012', cache_name=None, throttle=None, no_commit=False, ): from mptracker.scraper.people import MandateScraper http_session = create_session( cache_name=cache_name, throttle=throttle and float(throttle), ) mandate_scraper = MandateScraper(http_session) mandate_patcher = TablePatcher( models.Mandate, models.db.session, key_columns=['year', 'cdep_number'], ) with mandate_patcher.process() as add_mandate: for mandate in mandate_scraper.fetch(year): row = mandate.as_dict([ 'year', 'cdep_number', 'minority', 'college', 'constituency', ]) if year == '2012': end_date = mandate.end_date or date.max row['interval'] = DateRange(TERM_2012_START, end_date) person = ( models.Person.query .filter_by(name=mandate.person_name) .first()) if person is None: raise RuntimeError("Can't find person named %r" % mandate.person_name) row['person_id'] = person.id if not mandate.minority: county = ( models.County.query .filter_by(name=mandate.county_name) .first()) if county is None: raise RuntimeError("Can't match county name %r" % mandate.county_name) row['county'] = county add_mandate(row) if no_commit: logger.warn("Rolling back the transaction") models.db.session.rollback() else: models.db.session.commit()
def get_availability(begin, end, resources=None, duration=None): """ Availability is opening hours and free time between reservations This function calculates both for given time range Given a queryset of resources (even if just one) can calculate applicable opening hours and free time slots between begin and end of day and reservations for days that have reservations Does not currently check if resource is open shorter time than duration :param begin: :type begin: :param end: :type end: :param duration: :type duration: :param resources: :type resources: :return: :rtype: """ if not resources: resources = Resource.objects.all() dt_range = DateTimeTZRange(begin, end) if not begin < end: end_d = begin + datetime.timedelta(days=1) d_range = DateRange(begin.date, end_d.date()) else: d_range = DateRange(begin.date(), end.date()) # Saved query for overlapping periods, fetching also their Day items qs_periods = Period.objects.filter( duration__overlap=d_range ).order_by('exception').prefetch_related('days') # Saved query for Unit's overlapping periods, fetching also their Day items qs_unit_periods = Unit.objects.filter( periods__duration__overlap=d_range ).order_by('periods__exception').prefetch_related('periods__days') # Saved query for overlapping reservations qs_reservations = Reservation.objects.filter(duration__overlap=dt_range) # Get all resources and prefetch to stated attributes to the items # their matching Period, Unit and Reservation # items according to queries defined above resources_during_time = resources.prefetch_related( Prefetch('periods', queryset=qs_periods, to_attr='overlapping_periods'), Prefetch('unit', queryset=qs_unit_periods, to_attr='overlapping_unit'), Prefetch('reservations', queryset=qs_reservations, to_attr='overlapping_reservations') ) # NOTE: Resource's Period overrides Unit's Period opening_hours = {} availability = {} for res in resources_during_time: opening_hours[res] = periods_to_opening_hours(res, begin, end) if duration: availability[res] = calculate_availability(res, opening_hours[res], duration) return opening_hours, availability
def test_valid_dates(self): field = pg_forms.DateRangeField() value = field.clean(['01/01/2014', '02/02/2014']) lower = datetime.date(2014, 1, 1) upper = datetime.date(2014, 2, 2) self.assertEqual(value, DateRange(lower, upper))
class QuarterCurrentFactory(QuarterFactory): period = DateRange((date.today() + timedelta(days=-1)), (date.today() + timedelta(days=1)))
def _test_range_overlaps(self, constraint): # Create exclusion constraint. self.assertNotIn(constraint.name, self.get_constraints(HotelReservation._meta.db_table)) with connection.schema_editor() as editor: editor.add_constraint(HotelReservation, constraint) self.assertIn(constraint.name, self.get_constraints(HotelReservation._meta.db_table)) # Add initial reservations. room101 = Room.objects.create(number=101) room102 = Room.objects.create(number=102) datetimes = [ timezone.datetime(2018, 6, 20), timezone.datetime(2018, 6, 24), timezone.datetime(2018, 6, 26), timezone.datetime(2018, 6, 28), timezone.datetime(2018, 6, 29), ] HotelReservation.objects.create( datespan=DateRange(datetimes[0].date(), datetimes[1].date()), start=datetimes[0], end=datetimes[1], room=room102, ) HotelReservation.objects.create( datespan=DateRange(datetimes[1].date(), datetimes[3].date()), start=datetimes[1], end=datetimes[3], room=room102, ) # Overlap dates. with self.assertRaises(IntegrityError), transaction.atomic(): reservation = HotelReservation( datespan=(datetimes[1].date(), datetimes[2].date()), start=datetimes[1], end=datetimes[2], room=room102, ) reservation.save() # Valid range. HotelReservation.objects.bulk_create([ # Other room. HotelReservation( datespan=(datetimes[1].date(), datetimes[2].date()), start=datetimes[1], end=datetimes[2], room=room101, ), # Cancelled reservation. HotelReservation( datespan=(datetimes[1].date(), datetimes[1].date()), start=datetimes[1], end=datetimes[2], room=room102, cancelled=True, ), # Other adjacent dates. HotelReservation( datespan=(datetimes[3].date(), datetimes[4].date()), start=datetimes[3], end=datetimes[4], room=room102, ), ])
def get_calendar(self, operating_profile, operating_period): calendar_dates = [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), operation=False) for date_range in operating_profile.nonoperation_days ] calendar_dates += [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), special=True, operation=True) for date_range in operating_profile.operation_days ] for holiday in operating_profile.operation_bank_holidays: if holiday in BANK_HOLIDAYS: if (holiday == 'AllBankHolidays' or holiday == 'HolidayMondays') and self.region_id == 'S': continue date = BANK_HOLIDAYS[holiday] dates = DateRange(date, date, '[]') if operating_period.contains(date): calendar_dates.append( CalendarDate(start_date=date, end_date=date, dates=dates, special=True, operation=True)) else: self.undefined_holidays.add(holiday) for holiday in operating_profile.nonoperation_bank_holidays: if holiday in BANK_HOLIDAYS: if (holiday == 'AllBankHolidays' or holiday == 'HolidayMondays') and self.region_id == 'S': continue date = BANK_HOLIDAYS[holiday] dates = DateRange(date, date, '[]') if operating_period.contains(date): calendar_dates.append( CalendarDate(start_date=date, end_date=date, dates=dates, operation=False)) else: self.undefined_holidays.add(holiday) if operating_profile.servicedorganisation: org = operating_profile.servicedorganisation nonoperation_days = (org.nonoperation_workingdays and org.nonoperation_workingdays.working_days or org.nonoperation_holidays and org.nonoperation_holidays.holidays) if nonoperation_days: calendar_dates += [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), operation=False) for date_range in nonoperation_days ] operation_days = (org.operation_workingdays and org.operation_workingdays.working_days or org.operation_holidays and org.operation_holidays.holidays) if operation_days: calendar_dates += [ CalendarDate(start_date=date_range.start, end_date=date_range.end, dates=date_range.dates(), operation=True) for date_range in operation_days ] # remove date ranges which end before they start?! etc calendar_dates = [ dates for dates in calendar_dates if dates.relevant(operating_period) ] if not calendar_dates and not operating_profile.regular_days: return calendar_hash = f'{operating_profile.regular_days}{operating_period.dates()}' calendar_hash += ''.join(f'{date.dates}{date.operation}{date.special}' for date in calendar_dates) if calendar_hash in self.calendar_cache: return self.calendar_cache[calendar_hash] calendar = Calendar(mon=False, tue=False, wed=False, thu=False, fri=False, sat=False, sun=False, start_date=operating_period.start, end_date=operating_period.end, dates=operating_period.dates()) for day in operating_profile.regular_days: if day == 0: calendar.mon = True elif day == 1: calendar.tue = True elif day == 2: calendar.wed = True elif day == 3: calendar.thu = True elif day == 4: calendar.fri = True elif day == 5: calendar.sat = True elif day == 6: calendar.sun = True calendar.save() for date in calendar_dates: date.calendar = calendar CalendarDate.objects.bulk_create(calendar_dates) self.calendar_cache[calendar_hash] = calendar return calendar