class AddMatchesForm(forms.Form): eventtext = StrippedCharField(max_length=200, required=False, label=_('Event')) date = forms.DateField(required=True, label=_('Date')) game = forms.ChoiceField(choices=GAMES, label=_('Game version'), initial=LOTV) offline = forms.BooleanField(required=False, label=_('Offline'), initial=False) source = StrippedCharField(max_length=1000, required=False, label=_('Source')) contact = StrippedCharField(max_length=1000, required=False, label=_('Contact')) notes = forms.CharField(max_length=10000, required=False, label=_('Notes'), initial='') matches = forms.CharField(max_length=10000, required=True, label=_('Matches'), initial='') def __init__(self, is_adm, request=None): if request is not None: super(AddMatchesForm, self).__init__(request.POST) self.close_after = 'commit_close' in request.POST self.eobj = etn(lambda: request.POST['eventobj']) else: super(AddMatchesForm, self).__init__(initial={'date': date.today()}) self.close_after = False self.requested_close_after = self.close_after self.label_suffix = '' self.is_adm = is_adm self.fields['eventobj'] = forms.ChoiceField( choices=[(e['id'], e['fullname']) for e in Event.objects.filter( closed=False, type__in=(TYPE_ROUND, TYPE_EVENT)).annotate( num_downlinks=Count('downlink')).filter( num_downlinks=1).order_by('idx').values( 'id', 'fullname')], required=False, label=_('Event'), ) # Validation def clean_eventtext(self): if self.is_adm: return self.cleaned_data['eventtext'] if self.cleaned_data['eventtext'] in [None, '']: raise ValidationError(_('This field is required.')) return self.cleaned_data['eventtext'] def clean_source(self): if self.is_adm: return self.cleaned_data['source'] if self.cleaned_data['source'] in [None, '']: raise ValidationError(_('This field is required.')) return self.cleaned_data['source'] def clean(self): if self.is_adm: try: self.cleaned_data['eventobj'] = Event.objects.get( id=int(self.eobj)) except: raise ValidationError(_('Could not find this event object.')) return self.cleaned_data # Parse the matches def parse_matches(self, submitter): self.messages = [] if not self.is_valid(): self.messages.append( Message(_('Entered data was invalid, no changes made.'), type=Message.ERROR)) for field, errors in self.errors.items(): for error in errors: self.messages.append( Message(error=error, field=self.fields[field].label)) return [] if not self.is_adm: self.prematchgroup = PreMatchGroup( date=self.cleaned_data['date'], event=self.cleaned_data['eventtext'], source=self.cleaned_data['source'], contact=self.cleaned_data['contact'], notes=self.cleaned_data['notes'], game=self.cleaned_data['game'], offline=self.cleaned_data['offline'], ) self.prematchgroup.save() error_lines, matches = [], [] for line in self.cleaned_data['matches'].splitlines(): if line.strip() == '': continue try: parse_results = parse_match(line.strip(), allow_archon=False) pla_query = parse_results['pla'] plb_query = parse_results['plb'] sca = parse_results['sca'] scb = parse_results['scb'] make_flag = 'MAKE' in parse_results['flags'] dup_flag = 'DUP' in parse_results['flags'] except pyparsing.ParseException as e: self.messages.append( Message(_("Could not parse '%(line)s' (%(error)s).") % { 'line': line, 'error': str(e) }, type=Message.ERROR)) self.close_after = False error_lines.append(line) continue pla_race_override = find_race_override(pla_query) plb_race_override = find_race_override(plb_query) try: match = self.make_match( pla_query, plb_query, pla_race_override, plb_race_override, sca, scb, make_flag, dup_flag, ) if match is None: error_lines.append(line) continue except Exception as e: self.messages.append( Message(_("Error creating match '%(line)s' (%(error)s).") % { 'line': line, 'error': str(e) }, type=Message.ERROR)) self.close_after = False error_lines.append(line) continue if self.is_adm: match.submitter = submitter matches.append(match) for m in matches: m.save() if len(matches) > 0: self.messages.append( Message(ungettext_lazy('Successfully added %i match.', 'Successfully added %i matches.', len(matches)) % len(matches), type=Message.SUCCESS)) if self.close_after: self.cleaned_data['eventobj'].close() self.messages.append( Message(_("Closed '%s'.") % self.cleaned_data['eventobj'].fullname, type=Message.SUCCESS)) elif self.requested_close_after: self.messages.append( Message(_("Did not close '%s'.") % self.cleaned_data['eventobj'].fullname, type=Message.INFO)) self.data = self.data.copy() self.data['matches'] = '\n'.join(error_lines) return matches # Auxiliary function for searching for players def get_player(self, query, make_flag): players = find_player(lst=query, make=make_flag, soft=False) printable = ' '.join(str(x) for x in query) if players.count() != 1: if self.is_adm: if players.count() == 0: self.messages.append( Message(_("Could not find player: '%s'.") % printable, type=Message.ERROR)) self.close_after = False elif players.count() > 1: self.messages.append( Message(_("Ambiguous player: '%s'.") % printable, type=Message.ERROR)) self.close_after = False return None return players.first() # Make matches (called from parse_matches). DOES NOT SAVE THEM. def make_match(self, pla_query, plb_query, pla_race_or, plb_race_or, sca, scb, make_flag, dup_flag): pla = self.get_player(pla_query, make_flag) plb = self.get_player(plb_query, make_flag) if (pla is None or plb is None) and self.is_adm: return None if not self.is_adm: match = PreMatch( group=self.prematchgroup, pla=pla, plb=plb, pla_string=' '.join(pla_query), plb_string=' '.join(plb_query), sca=sca, scb=scb, date=self.cleaned_data['date'], rca=pla_race_or, rcb=plb_race_or, ) return match else: match = Match( pla=pla, plb=plb, sca=sca, scb=scb, rca=pla_race_or or (pla.race if pla.race != 'S' else 'R'), rcb=plb_race_or or (plb.race if plb.race != 'S' else 'R'), date=self.cleaned_data['date'], treated=False, eventobj=self.cleaned_data['eventobj'], game=self.cleaned_data['game'], offline=self.cleaned_data['offline'], ) if check_duplicates(match, dup_flag): self.messages.append( Message( _("Could not make match %(pla)s vs %(plb)s: possible duplicate found." ) % { 'pla': pla.tag, 'plb': plb.tag }, type=Message.ERROR, )) self.close_after = False return None if 'R' in [match.rca, match.rcb]: self.messages.append( Message( _("Unknown race in %(pla)s vs %(plb)s: set to random.") % { 'pla': pla.tag, 'plb': plb.tag }, type=Message.WARNING, )) match.set_period() match.set_ratings() return match
class AddMatchesForm(forms.Form): eventtext = StrippedCharField(max_length=200, required=False, label=_('Event')) date = forms.DateField(required=True, label=_('Date')) game = forms.ChoiceField(choices=GAMES, label=_('Game version'), initial=HOTS) offline = forms.BooleanField(required=False, label=_('Offline'), initial=False) source = StrippedCharField(max_length=1000, required=False, label=_('Source')) contact = StrippedCharField(max_length=1000, required=False, label=_('Contact')) notes = forms.CharField(max_length=10000, required=False, label=_('Notes'), initial='') matches = forms.CharField(max_length=10000, required=True, label=_('Matches'), initial='') def __init__(self, is_adm, request=None): if request is not None: super(AddMatchesForm, self).__init__(request.POST) self.close_after = 'commit_close' in request.POST self.eobj = etn(lambda: request.POST['eventobj']) else: super(AddMatchesForm, self).__init__(initial={'date': date.today()}) self.close_after = False self.requested_close_after = self.close_after self.label_suffix = '' self.is_adm = is_adm self.fields['eventobj'] = forms.ChoiceField( choices=[ (e['id'], e['fullname']) for e in Event.objects.filter(closed=False) .annotate(num_downlinks=Count('downlink')) .filter(num_downlinks=1) .order_by('idx') .values('id', 'fullname') ], required=False, label=_('Event'), ) # Validation def clean_eventtext(self): if self.is_adm: return self.cleaned_data['eventtext'] if self.cleaned_data['eventtext'] in [None, '']: raise ValidationError(_('This field is required.')) return self.cleaned_data['eventtext'] def clean_source(self): if self.is_adm: return self.cleaned_data['source'] if self.cleaned_data['source'] in [None, '']: raise ValidationError(_('This field is required.')) return self.cleaned_data['source'] def clean(self): if self.is_adm: try: self.cleaned_data['eventobj'] = Event.objects.get(id=int(self.eobj)) except: raise ValidationError(_('Could not find this event object.')) return self.cleaned_data # Parse the matches def parse_matches(self, submitter): self.messages = [] if not self.is_valid(): self.messages.append(Message(_('Entered data was invalid, no changes made.'), type=Message.ERROR)) for field, errors in self.errors.items(): for error in errors: self.messages.append(Message(error=error, field=self.fields[field].label)) return [] if not self.is_adm: self.prematchgroup = PreMatchGroup( date = self.cleaned_data['date'], event = self.cleaned_data['eventtext'], source = self.cleaned_data['source'], contact = self.cleaned_data['contact'], notes = self.cleaned_data['notes'], game = self.cleaned_data['game'], offline = self.cleaned_data['offline'], ) self.prematchgroup.save() error_lines, matches = [], [] for line in self.cleaned_data['matches'].splitlines(): if line.strip() == '': continue dashes = find_dashes(line) try: pla_query = shlex.split(line[:dashes[0]]) middle = shlex.split(line[dashes[0]+1:dashes[1]]) plb_query = middle[:-1] sca = int(middle[-1]) end = shlex.split(line[dashes[1]+1:]) scb = int(end[0]) make_flag = '!MAKE' in end dup_flag = '!DUP' in end except Exception as e: self.messages.append(Message( _("Could not parse '%(line)s' (%(error)s).") % {'line': line, 'error': str(e)}, type=Message.ERROR )) self.close_after = False error_lines.append(line) continue pla_race_override = find_race_override(pla_query) plb_race_override = find_race_override(plb_query) try: match = self.make_match( pla_query, plb_query, pla_race_override, plb_race_override, sca, scb, make_flag, dup_flag, ) if match is None: error_lines.append(line) continue except Exception as e: self.messages.append(Message( _("Error creating match '%(line)s' (%(error)s).") % {'line': line, 'error': str(e)}, type=Message.ERROR )) self.close_after = False error_lines.append(line) continue if self.is_adm: match.submitter = submitter matches.append(match) for m in matches: m.save() if len(matches) > 0: self.messages.append(Message( ungettext_lazy( 'Successfully added %i match.', 'Successfully added %i matches.', len(matches)) % len(matches), type=Message.SUCCESS )) if self.close_after: self.cleaned_data['eventobj'].close() self.messages.append( Message(_("Closed '%s'.") % self.cleaned_data['eventobj'].fullname, type=Message.SUCCESS)) elif self.requested_close_after: self.messages.append( Message(_("Did not close '%s'.") % self.cleaned_data['eventobj'].fullname, type=Message.INFO)) self.data = self.data.copy() self.data['matches'] = '\n'.join(error_lines) return matches # Auxiliary function for searching for players def get_player(self, query, make_flag): players = find_player(lst=query, make=make_flag, soft=False) if players.count() != 1: if self.is_adm: if players.count() == 0: self.messages.append( Message(_("Could not find player: '%s'.") % ' '.join(query), type=Message.ERROR)) self.close_after = False elif players.count() > 1: self.messages.append( Message(_("Ambiguous player: '%s'.") % ' '.join(query), type=Message.ERROR)) self.close_after = False return None return players.first() # Make matches (called from parse_matches). DOES NOT SAVE THEM. def make_match(self, pla_query, plb_query, pla_race_or, plb_race_or, sca, scb, make_flag, dup_flag): pla = self.get_player(pla_query, make_flag) plb = self.get_player(plb_query, make_flag) if (pla is None or plb is None) and self.is_adm: return None if not self.is_adm: match = PreMatch( group = self.prematchgroup, pla = pla, plb = plb, pla_string = ' '.join(pla_query), plb_string = ' '.join(plb_query), sca = sca, scb = scb, date = self.cleaned_data['date'], rca = pla_race_or, rcb = plb_race_or, ) return match else: match = Match( pla = pla, plb = plb, sca = sca, scb = scb, rca = pla_race_or or (pla.race if pla.race != 'S' else 'R'), rcb = plb_race_or or (plb.race if plb.race != 'S' else 'R'), date = self.cleaned_data['date'], treated = False, eventobj = self.cleaned_data['eventobj'], game = self.cleaned_data['game'], offline = self.cleaned_data['offline'], ) if check_duplicates(match, dup_flag): self.messages.append(Message( _("Could not make match %(pla)s vs %(plb)s: possible duplicate found.") % {'pla': pla.tag, 'plb': plb.tag}, type=Message.ERROR, )) self.close_after = False return None if 'R' in [match.rca, match.rcb]: self.messages.append(Message( _("Unknown race in %(pla)s vs %(plb)s: set to random.") % {'pla': pla.tag, 'plb': plb.tag}, type=Message.WARNING, )) match.set_period() match.set_ratings() return match