# -*- encoding: utf-8 -*- from django import forms from django.utils.translation import ugettext as _ from django.http import Http404 from drill.forms import GameSettings, Question from drill.models import Word, WordTranslation, Tag, Form from django.db.models import Q, Count from gamesettings.yamltools import yget import settings as S yfile = S.GAME_SETTINGS class YAMLSettings(object): def get(self, path): return yget(self.yamldict, path) def toGetTextTuples(self, opts): return [(a, _(b)) for a, b in opts] def taxonomies(self): taxes = self.yamldict['taxonomy'] choices = {} taxonomies = {} if taxes: for tax, opts in taxes.items(): proc_opts = self.toGetTextTuples(opts['choices']) self.__setattr__(tax, proc_opts) taxonomies[tax] = proc_opts else: taxonomies = False choices = False self.choices = choices self.taxonomies = taxonomies def questionAnswer(self): QAs = self.yamldict['questions'] self.Q_CHOICES = [(a['question']['abbr'], a['question']['name']) for a in QAs] # QA = [] # for item in QAs: # pass def question(self, abbr): Qs = self.yamldict['questions'] for question in Qs: if question['question']['abbr'] == abbr: return question return False def __init__(self, fname, gamename): import yaml with open(fname, 'r') as F: data = F.read() config_data = yaml.load(data) self.yamldict = config_data[gamename] or False self.taxonomies() self.questionAnswer() # parse YAML # question sets # taxonomies (create objects to set forms) class MorfaSettings(GameSettings): """ Morfa settings form. The idea here is to subclass this for the varying Morfa games. Subclasses, MorfaV, MorfaS, etc., should have fields for forms, and the following methods. .makeQuery() - constructs Q() & Q() type queries to filter words and tags. must set self.query optionally self.tag_query MorfaSettings then produces the initial data for formsets based on this query. """ select = {'widget': forms.Select} def __init__(self, *args, **kwargs): gametype = type(self).__name__ # MorfaS self.yaml = YAMLSettings(yfile, gametype) preset_fields = {} preset_fields['questiontype'] = forms.ChoiceField(initial=self.yaml.Q_CHOICES[0][0], choices=self.yaml.Q_CHOICES, **self.select) if self.yaml.taxonomies: for tax, opts in self.yaml.taxonomies.items(): t = self.yaml.get('taxonomy/%s' % tax) if 'choices' in t: fieldtype = forms.ChoiceField preset_fields[tax] = fieldtype(initial=opts[0][0], choices=opts, **self.select) # self.__setattr__(tax, field) kwargs['preset_fields'] = preset_fields super(MorfaSettings, self).__init__(*args, **kwargs) def makeQuery(self): """ Produces the query for nouns, setting self.query and self.tag_query for use by self.getInitialData. """ if self.is_valid(): options = self.cleaned_data else: options = self.initial_data self.question = self.yaml.question(abbr=options['questiontype']) if self.yaml.taxonomies: for tax, opts in self.yaml.taxonomies.items(): t = self.yaml.get('taxonomy/%s' % tax) qkwargs = {} if tax in options: qkwargs[t['attr']] = options[tax] else: qkwargs = {} QUERY = Q(**qkwargs) question_items = self.question['question'] or False answer_items = self.question['answer'] or False if question_items: filters = question_items['filters'] QUERY_Q = QUERY & Q(**filters) if answer_items: filters = answer_items['filters'] QUERY_A = QUERY & Q(**filters) # print 'QUERY: ', QUERY self.query_q = QUERY_Q self.query_a = QUERY_A def getPronoun(self, tag): return self.pronouns[tag.personnumber] or False def getInitialData(self): str_to_models = { 'Form': Form, 'Tag': Tag, 'Word': Word, } if 'pronouns' in self.question['answer']: p = self.question['answer']['pronouns'] if p: pron_path = self.question['answer']['pronouns'] self.pronouns = self.yaml.get(pron_path) else: self.pronouns = False else: self.pronouns = False model = str_to_models[self.yaml.get('data_model') or False] or False if model: if self.query_q: QUERY = self.query_q else: QUERY = self.query_a objects = model.objects.filter(QUERY) if objects.count() < 1: error = "Morfa.get_db_info: Database is improperly loaded.\ No tags for the query were found.\n\n" error += "Query: %s" % QUERY raise model.DoesNotExist(error) # TODO: max number here should come from global setting random_forms = objects.order_by('?')[0:5] inits = [] for form in random_forms: try: # TODO: NOMPL doesn't work with this. # TODO: questions for noun in PL always. if self.query_q: forms = form.word.form_set.filter(self.query_q) question_form = forms.order_by('?')[0] else: question_form = form.getBaseform(match_num=True) except Form.DoesNotExist: question_form = form.word.lemma question_lemma = question_form.fullform.lower() # TODO: better handling of non-generated forms try: forms = form.word.form_set.filter(self.query_a) answer_form = forms.order_by('?') if answer_form.count() == 0: raise Http404('Question type configured wrong, or forms do not\ exist for query.\n\ \n\ Query: %s' % self.query_a) else: answer_form = answer_form[0] answer_form = answer_form.matchNumber(question_form)[0] except Form.DoesNotExist: answer_form = form.word.lemma answer_lemma = answer_form.fullform.lower() app = {'id': form.word.id, 'lemma': question_lemma, # form.word.lemma, 'translations': [answer_lemma], } if self.pronouns: app['pronoun'] = self.getPronoun(answer_form.tag) inits.append(app) return inits class MorfaS(MorfaSettings): """ Morfa for substantive/nouns Fields for case, syllables and book. """ POS = "N" class MorfaV(MorfaSettings): POS = "V" class MorfaNum(MorfaSettings): POS = "Num" class MorfaAdj(MorfaSettings): POS = "Adj" ### class MorfaNum(MorfaSettings): ### """ Placeholder for stuff for MorfaNum ### TODO: Numra stuff. ### """ ### POS = 'Num' ### # num_bare = forms.ChoiceField(initial='N-ILL', choices=NUM_BARE_CHOICES, **select) ### # num_level = forms.ChoiceField(initial='1', choices=NUM_LEVEL_CHOICES, **select) ### # num_context = forms.ChoiceField(initial='NUM-ATTR', choices=NUM_CONTEXT_CHOICES, **select) ### ### def makeQuery(self): ### num_bare = options['num_bare'] ### num_level = options['num_level'] ### ### if self.is_valid(): ### options = self.cleaned_data ### else: ### options = self.initial_data ### ### _POS = Q(pos=self.POS) ### ### if options.has_key('num_level') and str(options['num_level']) == "1": ### smallnum = ["1","2","3","4","5","6","7","8","9","10"] ### QUERY = Q(pos__iexact=pos) & Q(presentationform__in=smallnum) ### else: ### QUERY = Q(pos__iexact=pos) ### return ### ### def getInitialData(self): ### """ Numra probably needs its own special subclass, since it actually uses the analyzers ### to generate data. ### ### For bonus brownie points we could cache all of the stuff it generates so it gets ### faster with use. """ ### pass ### ### ### ### class MorfaAdj(MorfaSettings): ### """ Stuff for MorfaAdj ### """ ### POS = 'A' ### select = {'widget': forms.Select} ### hide = {'widget': forms.HiddenInput()} ### ### casetable = { ### 'NOMPL' : 'Nom', ### # 'ATTR': 'Attr', ### 'N-GEN': 'Gen', ### 'N-PAR': 'Par', ### 'N-ILL': 'Ill', ### 'N-ELA': 'Ela', ### 'N-INE': 'Ine', ### 'N-ALL': 'All', ### 'N-ABL': 'Abl', ### 'N-ADE': 'Ade', ### 'N-TRA': 'Tra', ### 'N-ESS': 'Ess', ### 'N-ABE': 'Abe', ### '': '' ### } ### ### # adjcase = forms.ChoiceField(initial='N-PAR', choices=ADJCASE_CHOICES, **select) ### # adj_context = forms.ChoiceField(initial='N-PAR', choices=ADJ_CONTEXT_CHOICES, **select) ### # grade = forms.ChoiceField(initial='POS', choices=GRADE_CHOICES, **select) ### ### def makeQuery(self): ### if self.is_valid(): ### options = self.cleaned_data ### else: ### options = self.initial_data ### ### case = options['adjcase'] ### adj_context = options['adj_context'] ### grade = options['grade'] ### ### if case == "Attr": ### attributive = "Attr" ### case = "" ### ### if grade == "POS": grade = "Pos" ### if grade == "COMP": grade = "Comp" ### if grade == "SUPERL": grade = "Superl" ### ### _GRADE = Q(grade=grade) ### ### ## ### _POS = Q(pos=self.POS) ### _POSS = Q(possessive="") ### ### # syll = '' ### # if 'bisyllabic' in options: ### # syll = 'bisyllabic' ### # elif 'trisyllabic' in options: ### # syll = 'trisyllabic' ### # elif 'contracted' in options: ### # syll = 'contracted' ### ### # _SYLL = Q(stem__in=syll) ### ### # if 'book' in options: ### # book = options['book'] ### # else: ### # book = '' ### # ### # _SOURCE = Q(source__name=book) ### ### if 'adjcase' in options: ### case = self.casetable[options['adjcase']] ### _CASE = Q(case=case) ### ### numbers = ["Sg", "Pl", ""] ### if case == "Nom": numbers = ["Pl"] ### _NUMBER = Q(number__in=numbers) ### ### TAG_QUERY = _POS & _POSS & _CASE & _NUMBER & _GRADE ### QUERY = _POS # & _SYLL ### ### # if book and book not in ['all', 'All', '']: ### # QUERY = QUERY & _SOURCE ### ### self.query = QUERY ### self.tag_query = TAG_QUERY class MorfaQuestion(Question): """ Creates a Morfa question. Most of the functionality here is in parent classes, except for answer validation (check_answer). """ def check_answer(self): """ Gets analyses based on forms supplied and settingsform and compares to user input This question should be used in a Formset. """ # TODO: Spell relax answers = self.cleaned_data.get("answers") user_answer = self.cleaned_data.get("userans") if answers: if user_answer not in answers: self._errors["userans"] = self.Errors.try_again self.correct = False else: self.correct = True else: self._errors = self.Errors.empty_answers if not user_answer: self._errors["userans"] = self.Errors.blank_user_answer return