From 79612cca9ee1831bb85d4bdb07e4fb9d69f19e4f Mon Sep 17 00:00:00 2001 From: Matija Pretnar Date: Fri, 24 Mar 2023 19:38:10 +0100 Subject: [PATCH] Namesto GeneratedDataIncorrect uporabi validate --- CONTRIBUTING.md | 2 +- .../problems/models/eksponentna_funkcija.py | 10 ++-- nadlogar/problems/models/kompleksna.py | 8 ++- nadlogar/problems/models/kvadratna.py | 48 ++++++++---------- nadlogar/problems/models/linearna.py | 49 ++++++++++--------- nadlogar/problems/models/meta.py | 27 +++++----- nadlogar/problems/models/mnozice.py | 6 +-- nadlogar/problems/models/naravna.py | 17 +++---- nadlogar/problems/models/odvodi.py | 8 ++- nadlogar/problems/models/polinomi.py | 6 +-- nadlogar/problems/models/stoznice.py | 14 ++---- 11 files changed, 87 insertions(+), 108 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1dd425b..8eeb651 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ Za dodajanje nove vrste problema je potrebno: - V podrazredu `Meta` definirati spremenljivko `verbose_name` s pravilnim prikazom imena problema. - Definirati metodo `generate`, ki vrne slovar podatkov, ki jih lahko kasneje uporabite v navodilu in rešitvi naloge. -V metodi `generate` naključne vrednosti izbirate s pomočjo funkcij iz knjižnice `random`. Za nastavitev semena generatorja psevdonaključnih števil vam ni treba skrbeti. Če želite v problemu delati s simbolnimi izrazi, uporabite knjižnico `sympy`. Če želite lepo rešitev, je običajno bolje, da generirate najprej rešitev in nato nalogo, na primer najprej ničle polinoma in iz njih koeficiente ter ne obratno. Včasih se to ne da in za generiranje lepe rešitve potrebujete več poskusov. V tem primeru uporabite izjemo `GeneratedDataIncorrect`, ki jo sprožite, kadar podatki niso ustrezni. V tem primeru bo program izbral novo seme generatorja ter nalogo poskusil sestaviti znova. +V metodi `generate` naključne vrednosti izbirate s pomočjo funkcij iz knjižnice `random`. Za nastavitev semena generatorja psevdonaključnih števil vam ni treba skrbeti. Če želite v problemu delati s simbolnimi izrazi, uporabite knjižnico `sympy`. Če želite lepo rešitev, je običajno bolje, da generirate najprej rešitev in nato nalogo, na primer najprej ničle polinoma in iz njih koeficiente ter ne obratno. Včasih se to ne da in za generiranje lepe rešitve potrebujete več poskusov. V tem primeru uporabite metodo `validate`, ki preveri, ali so podatki ustrezni. V tem primeru bo program izbral novo seme generatorja ter nalogo poskusil sestaviti znova. Potem ko uspešno napišete tak razred, poženite ukaza: diff --git a/nadlogar/problems/models/eksponentna_funkcija.py b/nadlogar/problems/models/eksponentna_funkcija.py index 723339b..4b9b760 100644 --- a/nadlogar/problems/models/eksponentna_funkcija.py +++ b/nadlogar/problems/models/eksponentna_funkcija.py @@ -2,7 +2,7 @@ import sympy -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem def naredi_eksponentno(do=3, cela_osnova=False, premik=0): @@ -93,8 +93,8 @@ def generate(self): zamik_clena2 = random.choice([-3, -2, -1, 1, 2, 3]) k_clena2 = random.choice([1, 2, 3, -1, -2, -3]) resitev = random.choice([-1, 0, 1, 2, 3]) - if not (-2 < (resitev + zamik_clena1) and -2 < (resitev + zamik_clena2)): - raise GeneratedDataIncorrect + self.validate(-2 < (resitev + zamik_clena1)) + self.validate(-2 < (resitev + zamik_clena2)) vrednost = sympy.Rational( osnova ** (resitev + zamik_clena1) + k_clena2 * (osnova) ** (resitev + zamik_clena2) @@ -161,6 +161,6 @@ def generate(self): rational=True, ), ) - if not (max([abs(k_osnove2_levi), abs(k_osnove2_desni)]) < 50): - raise GeneratedDataIncorrect # Zagotovi, da v enačbi ne nastopajo prevelike vrednosti. + # Zagotovi, da v enačbi ne nastopajo prevelike vrednosti. + self.validate(max([abs(k_osnove2_levi), abs(k_osnove2_desni)]) < 50) return {"enacba": sympy.latex(enacba), "resitev": sympy.latex(resitev)} diff --git a/nadlogar/problems/models/kompleksna.py b/nadlogar/problems/models/kompleksna.py index 2c38207..8657299 100644 --- a/nadlogar/problems/models/kompleksna.py +++ b/nadlogar/problems/models/kompleksna.py @@ -3,7 +3,7 @@ import sympy from django.db import models -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem def generiraj_kompleksna_stevila(kolicina): @@ -17,10 +17,8 @@ def generiraj_kompleksna_stevila(kolicina): ) stevila = [r + i * sympy.I for r, i in zip(stevila_r, stevila_i)] - if len(stevila) != len( - set(stevila) - ): # preveri, da so vsa števila medsebojno različna - raise GeneratedDataIncorrect + # preveri, da so vsa števila medsebojno različna + Problem.validate(len(stevila) == len(set(stevila))) if kolicina == 1: stevila = stevila[0] diff --git a/nadlogar/problems/models/kvadratna.py b/nadlogar/problems/models/kvadratna.py index 93c2f2e..f1c9421 100644 --- a/nadlogar/problems/models/kvadratna.py +++ b/nadlogar/problems/models/kvadratna.py @@ -4,7 +4,7 @@ from django.db import models from .linearna import seznam_polovick, seznam_tretinj, skozi_tocki -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem def nicelna_oblika(od=-5, do=5, risanje=False): @@ -127,12 +127,12 @@ class Meta: def generate(self): (a, b, c, splosna) = splosna_oblika() - if not self.kompleksni_nicli: - if not (diskriminanta(a, b, c) >= 0 and abs(diskriminanta(a, b, c)) <= 200): - raise GeneratedDataIncorrect + D = diskriminanta(a, b, c) + self.validate(abs(D) <= 200) + if self.kompleksni_nicli: + self.validate(D < 0) else: - if not (diskriminanta(a, b, c) < 0 and abs(diskriminanta(a, b, c)) <= 200): - raise GeneratedDataIncorrect + self.validate(D >= 0) [x1, x2] = nicle(a, b, c) return { "splosna": sympy.latex(splosna), @@ -174,8 +174,7 @@ def generate(self): # (a, x1, x2, nicelna) = nicelna_oblika(-3, 3, risanje=True) # funkcija = sympy.expand(nicelna) # [p, q] = izracunaj_teme(a, -a * (x1 + x2), a * x1 * x2) -# if not (abs(q) <= 5): -# raise GeneratedDataIncorrect +# self.validate(abs(q) <= 5) # zacetna = funkcija.subs(x, 0) # return { # "funkcija": sympy.latex(funkcija), @@ -202,8 +201,8 @@ class Meta: def generate(self): (a, b, c, splosna) = splosna_oblika() D = diskriminanta(a, b, c) - if not (D >= 0 and abs(D) <= 200): - raise GeneratedDataIncorrect + self.validate(D >= 0) + self.validate(abs(D) <= 200) (p, q) = izracunaj_teme(a, b, c) return {"splosna": sympy.latex(splosna), "p": p, "q": q, "a": a} @@ -234,14 +233,12 @@ def generate(self): koeficienta = sympy.solve( (a * x1**2 + b * x1 + c - y1, a * x2**2 + b * x2 + c - y2), b, c ) - if not ( - koeficienta != [] - ): # Če ni rešitve vrne prazen seznam in ne praznega slovarja - raise GeneratedDataIncorrect - if not (abs(koeficienta[b]) < 5 and abs(koeficienta[c]) < 5): - raise GeneratedDataIncorrect - if not (abs(koeficienta[b]).q < 5 and abs(koeficienta[c]).q < 5): - raise GeneratedDataIncorrect + # Če ni rešitve vrne prazen seznam in ne praznega slovarja + self.validate(koeficienta != []) + self.validate(abs(koeficienta[b]) < 5) + self.validate(abs(koeficienta[c]) < 5) + self.validate(abs(koeficienta[b]).q < 5) + self.validate(abs(koeficienta[c]).q < 5) kvadratna = a * x**2 + koeficienta[b] * x + koeficienta[c] return { "parabola": sympy.latex(kvadratna), @@ -280,20 +277,16 @@ def generate(self): ) else: primerjava = splosna_oblika()[-1] - if not (splosna1 != primerjava): - raise GeneratedDataIncorrect + self.validate(splosna1 != primerjava) neenacaj = random.choice(["<", "<=", ">", ">="]) neenakost = sympy.Rel(splosna1, primerjava, neenacaj) nicli = sympy.solve(sympy.Eq(splosna1, primerjava), x) if len(nicli) == 0: pass else: - if not ( - nicli[0].is_real - and abs(max(nicli, key=abs)) < 10 - and sympy.denom(max(nicli, key=sympy.denom)) < 20 - ): - raise GeneratedDataIncorrect + self.validate(nicli[0].is_real) + self.validate(abs(max(nicli, key=abs)) < 10) + self.validate(sympy.denom(max(nicli, key=sympy.denom)) < 20) resitev = sympy.solveset(neenakost, domain=sympy.S.Reals) return {"neenakost": sympy.latex(neenakost), "resitev": sympy.latex(resitev)} @@ -328,8 +321,7 @@ def generate(self): x2 = random.randint(-5, 5) x3 = random.randint(-5, 5) - if not (len({x1, x2, x3}) == 3): - raise GeneratedDataIncorrect + self.validate(len({x1, x2, x3}) == 3) y1 = funkcija.subs(x, x1) y2 = funkcija.subs(x, x2) y3 = funkcija.subs(x, x3) diff --git a/nadlogar/problems/models/linearna.py b/nadlogar/problems/models/linearna.py index cc0d633..045e47d 100644 --- a/nadlogar/problems/models/linearna.py +++ b/nadlogar/problems/models/linearna.py @@ -3,7 +3,7 @@ import sympy from django.db import models -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem def seznam_polovick(od=-10, do=10): @@ -140,17 +140,19 @@ def generate(self): y1 = random.choice(seznam_polovick(-5, 5) + seznam_tretinj(-5, 5)) x2 = random.randint(-10, 10) y2 = random.randint(-10, 10) - if not ( - x1 != x2 and y1 != y2 - ): # Preveri, da sta 2 različni točki in nista vzporedni osem - raise GeneratedDataIncorrect - if not skozi_tocki(x1, y1, x2, y2)[0] in [ - sympy.Rational(x, y) - for x in range(0, 6) - for y in range(0, 2 * 10) - if (x != 0 and y != 0) - ]: # Lepše rešitve - raise GeneratedDataIncorrect + # Preveri, da sta 2 različni točki in nista vzporedni osem + self.validate(x1 != x2) + self.validate(y1 != y2) + # Lepše rešitve + self.validate( + skozi_tocki(x1, y1, x2, y2)[0] + in [ + sympy.Rational(x, y) + for x in range(0, 6) + for y in range(0, 2 * 10) + if (x != 0 and y != 0) + ] + ) premica = sympy.latex(skozi_tocki(x1, y1, x2, y2)[-1]) return { "x1": sympy.latex(x1), @@ -179,8 +181,8 @@ def generate(self): y1 = izberi_koordinato() x2 = izberi_koordinato() y2 = izberi_koordinato() - if not (x1 != x2 and y1 != y2): - raise GeneratedDataIncorrect + self.validate(x1 != x2) + self.validate(y1 != y2) razdalja = sympy.latex(razdalja_med_tockama(x1, y1, x2, y2)) return { "x1": sympy.latex(x1), @@ -256,8 +258,9 @@ def generate(self): y3 = 0 x = sympy.symbols("x") y = sympy.symbols("y") - if not (x2 != x1 and x2 != x3): # premici sta vzporedni - raise GeneratedDataIncorrect + # premici sta vzporedni + self.validate(x2 != x1) + self.validate(x2 != x3) if x2 == x1: premica1 = sympy.Eq(x, x1) else: @@ -347,8 +350,7 @@ def generate(self): x1 = random.choice(seznam_polovick(-3, 3) + seznam_tretinj(-3, 3)) x2 = random.choice(seznam_polovick(-3, 3) + seznam_tretinj(-3, 3)) - if not (x1 != x2): - raise GeneratedDataIncorrect + self.validate(x1 != x2) y1 = k * x1 + n y2 = k * x2 + n @@ -443,8 +445,8 @@ def generate(self): b = random.choice(izborCela) d = random.choice(izborCela) e = random.choice(izborCela) - if not ((a, b) != (d, e) and (x1 != 0 or y1 != 0)): - raise GeneratedDataIncorrect + self.validate((a, b) != (d, e)) + self.validate((x1 != 0 or y1 != 0)) c = a * x1 + b * y1 f = d * x1 + e * y1 enacba1 = a * x + b * y @@ -498,7 +500,7 @@ def generate(self): koef_y3 = random.choice(izborCela) koef_z3 = random.choice(izborCela) - if not ( + self.validate( len( { (koef_x1, koef_y1, koef_z1), @@ -507,9 +509,8 @@ def generate(self): } ) == 3 - and (x1 != 0 or y1 != 0 or z1 != 0) - ): - raise GeneratedDataIncorrect + ) + self.validate((x1 != 0 or y1 != 0 or z1 != 0)) # vrednosti enačb in enačbe vrednost1 = koef_x1 * x1 + koef_y1 * y1 + koef_z1 * z1 diff --git a/nadlogar/problems/models/meta.py b/nadlogar/problems/models/meta.py index 254b47f..291959a 100644 --- a/nadlogar/problems/models/meta.py +++ b/nadlogar/problems/models/meta.py @@ -63,14 +63,10 @@ class Template(string.Template): delimiter = "@" -class GeneratedDataIncorrect(Exception): - """An exception that is raised when a generator failed to produce proper problem data. - - Sometimes we can determine only after a few steps that the problem data in not suitable. - For example, the zeroes of a polynomial may be sufficiently small, but after expanding the - polynomial, the coefficients end up too large. In this case, we can raise - GeneratedDataIncorrect to restart the generator with a different random seed. - """ +class _GeneratedDataIncorrect(Exception): + # This is a private exception that is used to restart the generator with a different + # random seed. It is not meant to be used directly, but rather through the + # validate method. pass @@ -146,15 +142,16 @@ def generate(self): # generate anything. raise NotImplementedError + @classmethod def validate(self, condition): - """Raises GeneratedDataIncorrect if the condition is not met. + """Validates the generated data. - This is used to validate the generated data. If the data is not suitable, we - raise GeneratedDataIncorrect to restart the generator with a different random - seed. If the data is suitable, we do nothing. - """ + Sometimes we can determine only after a few steps that the problem data in not suitable. + For example, the zeroes of a polynomial may be sufficiently small, but after expanding the + polynomial, the coefficients end up too large. In this case, we can raise a private exception + to restart the generator with a different random seed.""" if not condition: - raise GeneratedDataIncorrect + raise _GeneratedDataIncorrect def _generate_data(self, seed): """Generates a list of problem data for all subproblems. @@ -177,7 +174,7 @@ def _generate_data(self, seed): if new_data not in data: data.append(new_data) break - except GeneratedDataIncorrect: + except _GeneratedDataIncorrect: pass return data diff --git a/nadlogar/problems/models/mnozice.py b/nadlogar/problems/models/mnozice.py index ab4f78a..c030f45 100644 --- a/nadlogar/problems/models/mnozice.py +++ b/nadlogar/problems/models/mnozice.py @@ -3,7 +3,7 @@ import sympy from django.db import models -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem class ElementiMnozice(Problem): @@ -123,10 +123,10 @@ def generate(self): k = sympy.symbols("k") a = random.randint(2, 5) b = random.randint(-4, 4) + self.validate(abs(b) != a) c = random.randint(2, 5) d = random.randint(-4, 4) - if abs(b) == a or abs(d) == c: - raise GeneratedDataIncorrect + self.validate(abs(d) != c) velikost_univerzalne = random.randint(12, 20) univerzalna = sympy.FiniteSet(*range(1, velikost_univerzalne + 1)) navodilo_A = a * k + b diff --git a/nadlogar/problems/models/naravna.py b/nadlogar/problems/models/naravna.py index 7b510e6..2dd4756 100644 --- a/nadlogar/problems/models/naravna.py +++ b/nadlogar/problems/models/naravna.py @@ -3,7 +3,7 @@ import sympy from django.db import models -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem class DeliteljVeckratnik(Problem): @@ -36,12 +36,11 @@ class Meta: def generate(self): stevilo1 = random.randint(self.minimalna_vrednost, self.maksimalna_vrednost) stevilo2 = random.randint(self.minimalna_vrednost, self.maksimalna_vrednost) - if not ( + self.validate(stevilo1 != stevilo2) + self.validate( max(*sympy.factorint(stevilo1).keys(), *sympy.factorint(stevilo2).keys()) <= self.maksimalni_prafaktor - and stevilo1 != stevilo2 - ): - raise GeneratedDataIncorrect + ) najvecji_delitelj = sympy.gcd(stevilo1, stevilo2) najmanjsi_veckratnik = sympy.lcm(stevilo1, stevilo2) @@ -65,11 +64,9 @@ class Meta: def generate(self): stevilo_malo = random.randint(50, 199) stevilo_veliko = random.randint(200, 1000) - if not ( - stevilo_veliko % stevilo_malo != 0 - and stevilo_malo % (stevilo_veliko % stevilo_malo) != 0 - ): # Da se ne konča že v prvih dveh korakih - raise GeneratedDataIncorrect + # Da se ne konča že v prvih dveh korakih + self.validate(stevilo_veliko % stevilo_malo != 0) + self.validate(stevilo_malo % (stevilo_veliko % stevilo_malo) != 0) najvecji_delitelj = sympy.gcd(stevilo_malo, stevilo_veliko) return { "stevilo1": stevilo_malo, diff --git a/nadlogar/problems/models/odvodi.py b/nadlogar/problems/models/odvodi.py index 416cdf7..f86ed23 100644 --- a/nadlogar/problems/models/odvodi.py +++ b/nadlogar/problems/models/odvodi.py @@ -3,7 +3,7 @@ import sympy -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem # Premisli, ali je ta razred smiselen, ker se tak tip podatkov načeloma shranjuje v slovarjih @@ -277,8 +277,7 @@ def generate(self): zunanja_funkcija = zunanja_funkcija.subs( x, random.choice([-3, -2, -1, 2, 3, 4, 5]) * x ) - if zunanja_funkcija == notranja_funkcija: - raise GeneratedDataIncorrect + self.validate(zunanja_funkcija != notranja_funkcija) funkcija = operator(zunanja_funkcija, notranja_funkcija) odvod = sympy.simplify(sympy.simplify(funkcija).diff(x)) @@ -399,8 +398,7 @@ def generate(self): funkcija1 = sympy.Poly([a, b1, c1], x).as_expr() funkcija2 = sympy.Poly([a, b2, c2], x).as_expr() presek = sympy.solve((funkcija1 - funkcija2), x) - if len(presek) != 1: - raise GeneratedDataIncorrect + self.validate(len(presek) == 1) k1 = funkcija1.diff().subs(x, *presek) k2 = funkcija2.diff().subs(x, *presek) kot = sympy.N(sympy.deg(kot_med_premicama(k1, k2))) diff --git a/nadlogar/problems/models/polinomi.py b/nadlogar/problems/models/polinomi.py index ea943e1..9fae3a9 100644 --- a/nadlogar/problems/models/polinomi.py +++ b/nadlogar/problems/models/polinomi.py @@ -2,7 +2,7 @@ import sympy -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem def seznam_polovic(od=-10, do=10): @@ -103,8 +103,8 @@ def generate(self): [a, b, c, splosna] = generiraj_splosno_obliko_kvadratne() [tretja_nicla, cetrta_nicla] = izracunaj_nicle_splosne_kvadratne(a, b, c) - if not (tretja_nicla != dvojna_nicla and cetrta_nicla != dvojna_nicla): - raise GeneratedDataIncorrect + self.validate(tretja_nicla != dvojna_nicla) + self.validate(cetrta_nicla != dvojna_nicla) polinom = sympy.expand(sympy.Mul((x - dvojna_nicla) ** 2, splosna)) return { diff --git a/nadlogar/problems/models/stoznice.py b/nadlogar/problems/models/stoznice.py index 9447ba5..0636ef5 100644 --- a/nadlogar/problems/models/stoznice.py +++ b/nadlogar/problems/models/stoznice.py @@ -3,7 +3,7 @@ import sympy from django.db import models -from .meta import GeneratedDataIncorrect, Problem +from .meta import Problem def razdalja_med_tockama(x1, y1, x2, y2): @@ -34,16 +34,14 @@ def generate(self): q2 = random.randint(-5, 5) x0 = random.randint(-5, 5) y0 = random.randint(-5, 5) - if (p1, q1) == (p2, q2): - raise GeneratedDataIncorrect + self.validate((p1, q1) != (p2, q2)) r1 = razdalja_med_tockama(x0, y0, p1, q1) r2 = razdalja_med_tockama(x0, y0, p2, q2) kroznica1 = sympy.Circle(sympy.Point(p1, q1), r1) kroznica2 = sympy.Circle(sympy.Point(p2, q2), r2) presek_kroznic = kroznica1.intersection(kroznica2) tocke_preseka = [(A.x, A.y) for A in presek_kroznic] - if (x0, y0) not in tocke_preseka: - raise GeneratedDataIncorrect + self.validate((x0, y0) in tocke_preseka) latex_zapis_tock = "" for id, tocka in enumerate(tocke_preseka): latex_zapis_tock += f"T_{id + 1} = {sympy.latex(tocka)}, " @@ -83,8 +81,7 @@ def generate(self): sredisce_elipse = sympy.Point(0, 0) vodoravna_polos = random.randint(1, 5) navpicna_polos = random.randint(1, 5) - if vodoravna_polos == navpicna_polos: - raise GeneratedDataIncorrect + self.validate(vodoravna_polos != navpicna_polos) teme = random.choice( [ sredisce_elipse.translate(x=vodoravna_polos), @@ -153,8 +150,7 @@ def generate(self): # b = random.randint(1, 4) # r = 1 # definiran r zaradi različnega izpisa krivulj # krivulja = ((x - p) ** 2) / a**2 + ((y - q) ** 2) / b**2 -# if a == b: -# raise GeneratedDataIncorrect +# self.validate(a != b) # razsirjena = a**2 * b**2 * krivulja.expand() - a**2 * b**2 # return {