def CompetitionToWcif(competition, schedule): output_dict = {} output_dict['formatVersion'] = '1.0' output_dict['id'] = competition.key.id() output_dict['name'] = competition.name wca_competition = competition.wca_competition.get() # TODO: add people rounds_by_event_id = collections.defaultdict(list) for r in ScheduleRound.query(ScheduleRound.schedule == schedule.key): rounds_by_event_id[r.event.id()].append(r) output_dict['events'] = [] for event_id, rounds in rounds_by_event_id.iteritems(): output_dict['events'].append(EventToWcif(event_id, rounds)) if schedule: output_dict['schedule'] = ScheduleToWcif(schedule, competition, wca_competition) extension_dict = {} extension_dict['datastoreId'] = competition.key.id() if competition.staff_signup_deadline: extension_dict['staffSignupDeadline'] = ( competition.staff_signup_deadline.strftime('%Y-%m-%d')) AddExtension('ScheduleCompetition', extension_dict, output_dict) return output_dict
def get(self, schedule_version): if not self.SetSchedule(int(schedule_version)): return template = JINJA_ENVIRONMENT.get_template( 'scheduling/edit_schedule.html') event_keys = set([ r.event for r in ScheduleRound.query( ScheduleRound.schedule == self.schedule.key).iter() ]) events = [ e.get() for e in sorted(event_keys, key=lambda e: e.get().rank) ] stages = (ScheduleStage.query( ScheduleRound.schedule == self.schedule.key).order( ScheduleRound.number).fetch()) self.response.write( template.render({ 'c': common.Common(self), 'competition': self.competition, 'schedule': self.schedule, 'events': events, 'stages': stages, 'new_stage_id': random.randint(2**4, 2**10), 'colors': sorted(Colors.keys()), }))
def GetImpl(handler, schedule, event): rounds = ScheduleRound.query( ndb.AND(ScheduleRound.schedule == schedule.key, ScheduleRound.event == event.key)).order( ScheduleRound.number).fetch() time_blocks_by_round = collections.defaultdict(list) for time_block in (ScheduleTimeBlock.query( ScheduleTimeBlock.schedule == schedule.key).order( ScheduleTimeBlock.start_time).iter()): time_blocks_by_round[time_block.round].append(time_block) stages = (ScheduleStage.query( ScheduleStage.schedule == schedule.key).order( ScheduleStage.number).fetch()) competition_days = [] current_date = schedule.start_date while current_date <= schedule.end_date: competition_days.append(current_date) current_date += datetime.timedelta(days=1) template = JINJA_ENVIRONMENT.get_template( 'scheduling/event_details.html') return template.render({ 'c': common.Common(handler), 'rounds': rounds, 'event': event, 'time_blocks': time_blocks_by_round, 'new_ids': { r.key: '%s_%d' % (r.key.id(), random.randint(2**4, 2**32)) for r in rounds }, 'stages': stages, 'competition_days': competition_days, })
def get(self, schedule_version, event_id): if not self.SetSchedule(int(schedule_version)): return event = Event.get_by_id(event_id) if not event: self.response.set_code(400) return rounds = ScheduleRound.query( ndb.AND(ScheduleRound.schedule == self.schedule.key, ScheduleRound.event == event.key)).order( ScheduleRound.number).fetch() time_blocks_by_round = collections.defaultdict(list) for time_block in (ScheduleTimeBlock.query( ScheduleTimeBlock.schedule == self.schedule.key).order( ScheduleTimeBlock.start_time).iter()): time_blocks_by_round[time_block.round].append(time_block) # Immediately after adding a TimeBlock, it may not have propagated to the datastore. # So we force retrieval of the just-added TimeBlock. if 'include_time_block' in self.request.GET: time_block = ScheduleTimeBlock.get_by_id( self.request.GET['include_time_block']) found = False for old_time_block in time_blocks_by_round[time_block.round]: if old_time_block.key == time_block.key: found = True if not found: time_blocks_by_round[time_block.round].append(time_block) time_blocks_by_round[time_block.round].sort( key=lambda tb: tb.GetStartTime()) stages = (ScheduleStage.query( ScheduleStage.schedule == self.schedule.key).order( ScheduleStage.number).fetch()) competition_days = [] current_date = self.schedule.start_date while current_date <= self.schedule.end_date: competition_days.append(current_date) current_date += datetime.timedelta(days=1) template = JINJA_ENVIRONMENT.get_template( 'scheduling/event_details.html') self.response.write( template.render({ 'c': common.Common(self), 'rounds': rounds, 'event': event, 'time_blocks': time_blocks_by_round, 'new_ids': { r.key: '%s_%d' % (r.key.id(), random.randint(2**4, 2**32)) for r in rounds }, 'stages': stages, 'competition_days': competition_days, }))
def post(self, schedule_version): if not self.SetSchedule(int(schedule_version)): return r = ScheduleRound.get_by_id(self.request.POST['round-id']) if not r: self.response.set_status(400) return r.num_groups = int(self.request.POST['num-groups']) r.num_staff_groups = int(self.request.POST['num-staff-groups']) r.put() self.response.write( EventDetailsHandler.GetImpl(self, self.schedule, r.event.get()))
def ScheduleToWcif(schedule, competition, wca_competition): output_dict = {} if schedule.start_date: output_dict['startDate'] = schedule.start_date.strftime('%Y-%m-%d') output_dict['numberOfDays'] = ( (schedule.end_date - schedule.start_date).days + 1) # The CubingUSA scheduling system is not designed to support competitions # with more than one venue. venue_dict = {} venue_dict['id'] = 0 # TODO: pass along the venue information from the WCA export. if wca_competition: venue_dict['latitude'] = wca_competition.latitude / 1000000. venue_dict['longitude'] = wca_competition.longitude / 1000000. venue_dict['timezone'] = competition.timezone all_stages = ScheduleStage.query( ScheduleStage.schedule == schedule.key).fetch() all_rounds = ScheduleRound.query( ScheduleRound.schedule == schedule.key).fetch() time_blocks_by_stage = collections.defaultdict(list) for t in (ScheduleTimeBlock.query( ScheduleTimeBlock.schedule == schedule.key).order( ScheduleTimeBlock.start_time).iter()): time_blocks_by_stage[t.stage.id()].append(t) groups_by_stage_and_time_block = ( collections.defaultdict(lambda: collections.defaultdict(list))) for g in (ScheduleGroup.query( ScheduleGroup.schedule == schedule.key).order( ScheduleGroup.start_time).iter()): groups_by_stage_and_time_block[g.stage.id()][g.time_block.id()].append( g) venue_dict['rooms'] = [] for stage in all_stages: venue_dict['rooms'].append( StageToWcif(stage, time_blocks_by_stage[stage.key.id()], groups_by_stage_and_time_block[stage.key.id()])) output_dict['venues'] = [venue_dict] extension_dict = {} extension_dict['datastoreId'] = schedule.key.id() extension_dict['creationTime'] = (ToLocalizedTime( schedule.creation_time, competition.timezone).isoformat()) extension_dict['lastUpdateTime'] = (ToLocalizedTime( schedule.last_update_time, competition.timezone).isoformat()) AddExtension('Schedule', extension_dict, output_dict) return output_dict
def get(self, schedule_version): if not self.SetSchedule(int(schedule_version)): return existing_groups = ScheduleGroup.query( ScheduleGroup.schedule == self.schedule.key).fetch(keys_only=True) time_blocks_by_round = collections.defaultdict(list) rounds = {} for time_block in ScheduleTimeBlock.query( ScheduleTimeBlock.schedule == self.schedule.key).iter(): time_blocks_by_round[time_block.round].append(time_block) for r in ScheduleRound.query( ScheduleRound.schedule == self.schedule.key).iter(): rounds[r.key] = r groups_to_put = [] for round_key in rounds: r = rounds[round_key] if not r.num_groups: continue time_blocks = time_blocks_by_round[round_key] time_blocks_by_staff_only = collections.defaultdict(list) for time_block in time_blocks: time_blocks_by_staff_only[time_block.staff_only].append( time_block) num_by_stage = collections.defaultdict(lambda: 0) round_groups = CreateGroups( self.schedule, time_blocks_by_staff_only[False], r, num_by_stage, r.num_groups - (r.num_staff_groups or 0)) if time_blocks_by_staff_only[True]: num_by_stage = collections.defaultdict(lambda: 0) round_groups.extend( CreateGroups(self.schedule, time_blocks_by_staff_only[True], r, num_by_stage, r.num_staff_groups)) groups_to_put.extend(round_groups) ndb.delete_multi(existing_groups) ndb.put_multi(groups_to_put) self.schedule.last_update = datetime.datetime.now() self.schedule.put() self.redirect( webapp2.uri_for('edit_schedule', competition_id=self.competition.key.id(), schedule_version=self.schedule.key.id()))
def __init__(self, user, competition, schedule=None): self.user = user self.competition = competition if schedule: self.schedule = schedule else: self.schedule = Schedule.query( ndb.AND(Schedule.competition == competition.key, Schedule.is_live == True)).get() if not self.schedule: return self.events = {} self.event_keys = [] self.stage_ids = set() for r in ScheduleRound.query( ScheduleRound.schedule == self.schedule.key).iter(): if r.event.id() not in self.events: self.events[r.event.id()] = EventDetails(r.event) self.event_keys.append(r.event) self.events[r.event.id()].AddRound(r) for t in (ScheduleTimeBlock.query( ndb.AND(ScheduleTimeBlock.schedule == self.schedule.key, ScheduleTimeBlock.staff_only == False)).order( ScheduleTimeBlock.start_time).iter()): self.events[t.round.get().event.id()].AddTimeBlock(t) self.stage_ids.add(t.stage.id()) if user and user.wca_person: self.ranks_single = { r.event.id(): r for r in RankSingle.query( RankSingle.person == user.wca_person).iter() } self.ranks_average = { r.event.id(): r for r in RankAverage.query( RankAverage.person == user.wca_person).iter() } else: self.ranks_single = {} self.ranks_average = {} self.has_qualifying_number = False
def get(self): OAuthBaseHandler.get(self) if not self.auth_token: return competition_id = self.handler_data if not self.SetCompetition(competition_id): return response = self.GetWcaApi('/api/v0/competitions/%s/wcif' % competition_id) if response.status != 200: self.redirect(webapp2.uri_for('index', unknown=1)) return response_json = json.loads(response.read()) objects_to_put = [] schedule = Schedule() schedule.competition = self.competition.key schedule.creation_time = datetime.datetime.now() schedule.last_update_time = schedule.creation_time schedule.is_live = False schedule.put() for event in response_json['events']: event_key = ndb.Key(Event, event['id']) round_num = 0 next_round_count = 0 for round_json in event['rounds']: round_num += 1 round_object = ScheduleRound(id=ScheduleRound.Id( schedule.key.id(), event['id'], round_num)) round_object.schedule = schedule.key round_object.event = event_key round_object.number = round_num round_object.is_final = len(event['rounds']) == round_num round_object.format = ndb.Key(Format, round_json['format']) if round_json['cutoff']: round_object.cutoff = round_json['cutoff']['attemptResult'] if round_json['timeLimit'] and round_json['timeLimit'][ 'centiseconds']: round_object.time_limit = round_json['timeLimit'][ 'centiseconds'] round_object.wcif = json.dumps(round_json) if next_round_count: round_object.num_competitors = next_round_count objects_to_put.append(round_object) advancement_condition = round_json['advancementCondition'] if advancement_condition and advancement_condition[ 'type'] == 'ranking': next_round_count = advancement_condition['level'] else: next_round_count = 0 ndb.put_multi(objects_to_put) self.redirect( webapp2.uri_for('edit_schedule', schedule_version=schedule.key.id()))
def ImportTimeBlock(activity_data, schedule, stage, out, time_blocks, groups): if 'id' not in activity_data: out.errors.append('activityCode missing from Activity.') return if 'activityCode' not in activity_data: out.errors.append('activityCode missing from Activity %d.' % activity_data['id']) return try: activity_code = ActivityCode.ParseCode(activity_data['activityCode']) except Exception as e: out.errors.append(e.message) return if activity_code.other_string: # TODO: support non-event Activities. return if not activity_code.event_id: out.errors.append('Missing event ID for activity %s' % activity_data['activityCode']) return if not activity_code.round_id: # We ignore Activities that are not specific to a round. return if 'startTime' not in activity_data: out.errors.append('Missing startTime for activity %s' % activity_data['activityCode']) return if 'endTime' not in activity_data: out.errors.append('Missing endTime for activity %s' % activity_data['activityCode']) return extension = GetExtension('ScheduleTimeBlock', activity_data) round_id = ScheduleRound.Id(schedule.key.id(), activity_code.event_id, activity_code.round_id) time_block_id = '%s_%d' % (round_id, activity_data['id']) if time_block_id in time_blocks: time_block = time_blocks.pop(time_block_id) else: time_block = ScheduleTimeBlock(id=time_block_id) time_block.schedule = schedule.key time_block.round = ndb.Key(ScheduleRound, round_id) time_block.stage = stage.key if activity_code.attempt: time_block.attempt = activity_code.attempt time_block.start_time = timezones.StripTimezone( dateutil.parser.parse(activity_data['startTime'])) time_block.end_time = timezones.StripTimezone( dateutil.parser.parse(activity_data['endTime'])) if 'staffOnly' in extension: time_block.staff_only = extension['staffOnly'] if 'childActivities' in activity_data: for child_activity in activity_data['childActivities']: ImportData(child_activity, time_block, out, groups) out.entities_to_put.append(time_block)
def ImportEvents(wcif_data, schedule_key, out): existing_rounds = { e.key.id(): e for e in ScheduleRound.query( ScheduleRound.schedule == schedule_key).iter() } all_event_ids = set([e.key.id() for e in Event.query().iter()]) all_formats = set([f.key.id() for f in Format.query().iter()]) for event in wcif_data['events']: if 'id' not in event: out.errors.append( 'Malformed WCIF: missing \'id\' field for event.') continue if event['id'] not in all_event_ids: out.errors.append('Unrecognized event ID \'%s\'' % event['id']) continue event_key = ndb.Key(Event, event['id']) round_num = 0 next_round_count = 0 for round_json in event['rounds']: round_num += 1 round_id = ScheduleRound.Id(schedule_key.id(), event['id'], round_num) if round_id in existing_rounds: round_object = existing_rounds.pop(round_id) else: round_object = ScheduleRound(id=round_id) round_object.schedule = schedule_key round_object.event = event_key round_object.number = round_num round_object.is_final = len(event['rounds']) == round_num if 'format' not in round_json: out.errors.append( 'Malformed WCIF: missing \'format\' field for %s' % round_id) continue if round_json['format'] not in all_formats: out.errors.append('Unrecognized format ID \'%s\'' % round_json['format']) continue round_object.format = ndb.Key(Format, round_json['format']) if 'cutoff' in round_json and round_json['cutoff']: round_object.cutoff = round_json['cutoff']['attemptResult'] if ('timeLimit' in round_json and round_json['timeLimit'] and 'centiseconds' in round_json['timeLimit'] and round_json['timeLimit']['centiseconds']): round_object.time_limit = round_json['timeLimit'][ 'centiseconds'] round_object.wcif = json.dumps(round_json) if next_round_count: round_object.num_competitors = next_round_count next_round_count = 0 if 'advancementCondition' in round_json: advancement_condition = round_json['advancementCondition'] if (advancement_condition and 'type' in advancement_condition and advancement_condition['type'] == 'ranking'): next_round_count = advancement_condition['level'] out.entities_to_put.append(round_object) out.entities_to_delete.extend([r for r in existing_rounds.itervalues()]) # Also look for time blocks and groups that are now unused. for obj_class in (ScheduleTimeBlock, ScheduleGroup): for obj in obj_class.query(obj_class.schedule == schedule_key).iter(): if obj.round.id() in existing_rounds: out.entities_to_delete.append(obj)
def get(self, competition_id): # Unlike most scheduling handlers, it's okay here if the competition doesn't # exist, because that may just mean the user is creating the competition # here for the first time. In this case redirect to /update. if not ScheduleCompetition.get_by_id(competition_id): self.redirect_to('update_competition', competition_id=competition_id) return if not self.SetCompetition(competition_id): return timezones_and_times = [ (timezone, datetime.datetime.now( pytz.timezone(timezone)).strftime('%I:%M %p')) for timezone in pytz.country_timezones('us') ] schedule_versions = Schedule.query( Schedule.competition == self.competition.key).fetch() # We look at the live schedule, or the most-recently updated one, and make # sure that it has events and start/end dates. schedule_for_staff_signup = None has_live_schedule = False if schedule_versions: for schedule in schedule_versions: if schedule.is_live: schedule_for_staff_signup = schedule has_live_schedule = True if not schedule_for_staff_signup: schedule_for_staff_signup = sorted( schedule_versions, key=lambda s: s.last_update_time)[-1] has_rounds = (ScheduleRound.query( ScheduleRound.schedule == schedule_for_staff_signup.key).iter().has_next()) championships = Championship.query(Championship.competition == ndb.Key( Competition, competition_id)).fetch() if championships: if championships[0].residency_deadline: residency_deadline = timezones.ToLocalizedTime( championships[0].residency_deadline, self.competition.timezone or 'America/New_York') else: # If the residency deadline hasn't been set, use the day before the # competition starts. residency_deadline = ( datetime.datetime.combine( championships[0].competition.get().start_date, datetime.time(0, 0, 0)) - datetime.timedelta(days=1)).replace(tzinfo=pytz.timezone( self.competition.timezone or 'America/New_York')) else: residency_deadline = None template = JINJA_ENVIRONMENT.get_template( 'scheduling/edit_competition.html') self.response.write( template.render({ 'c': common.Common(self), 'competition': self.competition, 'timezones_and_times': timezones_and_times, 'schedule_versions': schedule_versions, 'staff_signup_enabled': schedule_for_staff_signup and schedule_for_staff_signup.start_date and has_rounds, 'has_live_schedule': has_live_schedule, 'residency_deadline': residency_deadline }))