Javscript

Eleventy ve Eleventy Serverless ile Test Oluşturma

Birkaç gün önce, bir “quiz” in nasıl görüneceğini düşünüyordum. onbir. Bunu tırnak içine aldım çünkü sınavları ve nasıl oluşturulduklarını düşünmenin birçok farklı yolu var. Demom için özellikler listemi aşağıdaki gibi ayarladım:

  • Bir sınavı bir dizi soru olarak tanımladım. Her sorunun bir doğru cevabı vardır.
  • Testler JSON’da tanımlanacak ve yalnızca bir veri dosyasına bırakarak yeni testler eklemenize izin verecekti. “Gerçek dünyada”, JSON dosyalarının nasıl kalıcı oldukları ile test verilerini eklemek/düzenlemek/silmek için web’i kullanan kullanıcı dostu bir düzenleyici hayal edebileceğinizi düşünüyorum. Veya onları bir veritabanında saklayın ve tablo verilerini JSON’a aktarma işlemine sahip olun.
  • İstemci tarafı JavaScript’te bir sınav yapılabilirken, JavaScript olmayan, saf bir HTML yaklaşımı istedim. Bu nedenle, testimiz sadece soru listesini görüntüleyecek, bir Eleveny Sunucusuz işlemi yapın ve sonucu döndürün.
  • Bir sınav aslında bir formdur ve bir “dinamik form oluşturucu” oluşturmak, tavşan deliğinden aşağı inmenin tam tanımı olabilir. İşleri basit tutmak için tek seçenekli soruları, çoktan seçmeli soruları ve doğru/yanlış sorularını desteklemeye karar verdim. Dallanma veya koşullu mantık yok, sadece üç tür soru var.

Bunu akılda tutarak, ne inşa ettiğimi tarif edeyim.

Kısa Sınavları Tanımlama

Yukarıda da söylediğim gibi desteklediğim şeyler açısından oldukça basit bir quiz kurulumu ile gittim. Yalnızca üç tür soru vardır: tek seçenekli, çoktan seçmeli ve doğru/yanlış. Bir testin, kendisini tanımlamaya yardımcı olması için bir adı ve açıklaması ve ardından bir soru listesi olmalıdır. İşte JSON’da yerleşik bir örnek sınav:

{
	"name":"Alpha",
	"description":"A quiz on understanding alpha.",
	"questions":[
		{
			"text":"What is the meaning of life?",
			"answers":[
				"42",
				"21",
				"beer"
			],
			"correctAnswer":0
		},
		{
			"text":"What is question two?",
			"type":"multiple",
			"answers":[
				"moon",
				"sun",
				"stars"
			],
			"correctAnswer":[0,1]
		},

		{
			"text":"Is Eleventy awesome?",
			"type":"truefalse",
			"correctAnswer":true
		}

	]
}

Her sorunun en az bir metin değeri olmalıdır. bu type değer varsayılan olarak single yani bırakılabilir. “Doğru/yanlış” türleri dışında her sorunun bir dizi yanıtı vardır. Sonunda kullanıyoruz correctAnswer sorunun doğru cevabını belirlemek için. Şunun için not edin: multiple, bu bir dizi. (Bir öğe dizisine sahip olsanız da.)

Kısa sınavları dosya yapısı açısından sitenin geri kalanından ayrı tutmak için adında bir klasör oluşturdum. quizzes ve oraya birkaç json dosyası bıraktı.

Sınavlar Yükleniyor

Kısa sınavlar oluşturmak için tanımlanmış bir format ile bunları nasıl yükleriz? ben yarattım _data adlı dosya quizzes.js:

const fs = require('fs');

const inputDir="./quizzes";

module.exports = function() {

	let quizzes = [];
	let files = fs.readdirSync(inputDir);

	files.forEach(f => {
		if(f.split('.').pop() === 'json') {

			let contents = JSON.parse(fs.readFileSync(inputDir + "https://www.raymondcamden.com/" + f, 'utf8'));
			// todo: Validate contents - perhaps via JSON schema?
			/*
			One thing we can do now, we let people leave off type for "single" questions as
			they will be the most common, but easier outside of here to specify. So let's fix it.
			*/
			contents.questions.forEach(q => {
				if(!q.type) q.type = "single";
			});
			quizzes.push(contents);
		}
	});

	return quizzes;
}

Yukarıdan aşağıya – sınavlar dizinimi okuyarak başladığını görebilirsiniz. Her birini okur ve JSON’u ayrıştırır. Yorumlarda belirtildiği gibi, bu bir harika faydalanmak için yer JSON Şeması. Bu, sınavları düzenlemek için kod ipuçları vermeme izin vermekle kalmaz (dediğim gibi, teknik olmayan kullanıcılar için web tabanlı bir editör hayal etsem de), sınavları yüklemeye çalışmadan önce doğrulamama izin verir.

Kodum küçük bir işlem yapar ve varsayılan değerini işler. single ne zaman type sorular için tanımlanmamıştır.

Sonunda, şimdi bir quizzes verilerde mevcut nesne.

Test Oluşturma

Tamam, bu karmaşık kısım. Testlerimi üç farklı soru türünde tutmama rağmen, bunu halletmek için hala biraz kod gerekiyor. Eleventy’s’i kullandım sayfalandırma sınav başına bir sayfa oluşturma özelliği:

---
pagination:
    data: quizzes
    size: 1
    alias: quiz
permalink: "quiz/{{ quiz.name | slugify }}/"
eleventyComputed:
    title: "{{quiz.name}}"
layout: main
---

<h2>Quiz: {{ quiz.name }}</h2>
<p>
{{ quiz.description }} 
</p>

<form method="get" action="/submitQuiz/">
<input type="hidden" name="quiz" value="{{ quiz.name | slugify }}">
{% for question in quiz.questions %}
	<h3>{{ forloop.index }}. {{ question.text }}</h3>

	{% assign qindex = forloop.index %}

	{% if question.type == "single" %}

		<p>
		{% for answer in question.answers %}
		<input type="radio" name="q{{qindex}}" id="q{{qindex}}_{{forloop.index}}" value="{{forloop.index}}" > 
		<label for="q{{qindex}}_{{forloop.index}}">{{ answer }}</label><br/>
		{% endfor %}
		</p>

	{% elsif question.type == "multiple" %}

		<p>
		{% for answer in question.answers %}
		<input type="checkbox" name="q{{qindex}}" id="q{{qindex}}_{{forloop.index}}" value="{{forloop.index}}">
		<label for="q{{qindex}}_{{forloop.index}}">{{ answer }}</label><br/>
		{% endfor %}
		</p>
		

	{% elsif question.type == "truefalse" %}

		<p>
		<input type="radio" name="q{{qindex}}" id="q{{qindex}}_0" value="true"> 
		<label for="q{{qindex}}_0">True</label><br/>
		<input type="radio" name="q{{qindex}}" id="q{{qindex}}_1" value="false"> 
		<label for="q{{qindex}}_1">False</label><br/>
		</p>

	{% endif %}

{% endfor %}

<p>
<input type="submit" value="Submit Answers">
</p>

</form>

Yukarıdan, her bir öğe için şunu belirtiyorum: quizzes veri dizisi Tek bir benzersiz dosyam olmalı. Yol, sınavlardan geçen sınavların adına dayalıdır. slugify filtre. Formumun gönderildiğine dikkat edin submitQuiz bundan sonra ele alacağım. Ayrıca belirttiğime dikkat edin method="get". Netlify Sunucusuz işlevleri POST parametrelerini almadığından bu gereklidir (not – Bunu iki kez kontrol ediyorum ve yanılıyor olabilirim).

Formun içinde, her sorunun üzerinden geçiyorum. Gerçek karmaşıklığın olduğu yer burasıdır. Mevcut döngü indeksini alarak başlıyorum. Sorularımı formda “adlandırabilmem” için buna ihtiyacım var. Her birinin adlandırıldığını göreceksiniz q{{qIndex}} hangi çıkış yapacak q1, q2ve benzeri.

Daha sonra her cevaba benzersiz bir kimlik değeri veririm. Bu, soru adına ve her bir cevabın dizinine dayanmaktadır. Yani örneğin, ilki q1_1. Bunu her cevap için bir etiket atayabilmek için yapıyorum. (Yalnızca son zamanlarda atlayabileceğinizi keşfettim for alanı ve metni içeri sararsanız <label> etiketler. Muhtemelen bunu işleri daha basit tutmak için yapardım.) Her cevap için dinamik metnin çıktısını alıyorum. Son olarak, cevaplar dinamik olmadığı için “doğru/yanlış” olan biraz daha statiktir.

İşte ilk sınav görüntülerim:

Sınavları Kontrol Etme

Son adım, kullanıcı girdisini almayı ve ne kadar iyi yaptıklarını kontrol etmeyi içerir. Yukarıdan hatırlarsanız, sınavım şunu gösteriyor: /submitQuiz/. Bunun için bir Eleventy Serverless işlevi kullanacağım. (Nasıl çalıştığı konusunda bilgi tazelemeye ihtiyacınız varsa, temel dokümanlar aynı zamanda benim giriiş.)

Eleventy Serverless desteğini desteklemek için yönergeleri takip ettim ve ardından şablonumu oluşturdum:

---
layout: main
permalink:
    serverless: /submitQuiz/
---


{% assign results = eleventy.serverless.query | checkQuiz: quizzes %}

<h2>Quiz: {{ results.quizName }}</h2>

<p>
Out of {{ results.totalQuestions }} questions, you got {{ results.correct }} answers correct. 
That's a percentage of {{ results.percentage }}. 
</p>

{% if results.percentage > 80 %}
<p>
<strong>Congrats on a great score!</strong>
</p>
{% endif %}

Oldukça kısa çünkü mantığın çoğu başka yerde yapılıyor. Onbir Sunucusuz şablon, forma dayalı olarak aşağıdaki gibi görünen sorgu dizesine erişebilir:

http://localhost:42357/submitQuiz/?quiz=alpha&q1=3&q2=1&q2=2&q3=true

Testin adını (sümüklü) ve cevapları aldım. Çoktan seçmeli bir seçenek için soru adının tekrarlandığını unutmayın. Bu birazdan önemli olacak.

Sonuçları almak için sorgu nesnesini ve sınavlarımı adlı bir filtreye iletirim. checkQuiz. Tüm gerçek iş burada yapılır Test için toplam soru sayısını, ne kadar doğru yaptığınızı ve yüzdeyi verir. (Şablonda yapılabilirdi, ama iyi olacağımı ve sizin için yapacağımı düşündüm.)

Son olarak, iyi puan alırsanız küçük bir tebrik mesajı ekleyeceğim. Şu filtreye bir göz atalım:

eleventyConfig.addFilter('checkQuiz', (submission, quizzes) => {
	/*
	submission is the query string from the submitted quiz, it needs to have name at minimum
	*/
	// first, match up the slugged name to our quizes
	let quiz = quizzes.find(q => {
		if(eleventyConfig.getFilter('slugify')(q.name) === submission.quiz) return true;
		return false;
	});
	let correct = 0;
	quiz.questions.forEach((question,idx) => {
		/*
		validate based on type

		note that the html uses a 1 based index, json data for quiz is 0 based
		1 based index is used for answers, so q1 is for question 0
		*/
		let myanswer="";
		if(submission['q' + (idx + 1)]) {
			myanswer = submission['q' + (idx + 1)];
		}

		if(question.type === "single") {
			if(question.correctAnswer === myanswer-1) correct++;
		} else if(question.type === "multiple") {
			/*
			myanswer will either be an empty string or a list: X,Y. can't assume order
			will be right, so basically: length of items of MY answer must match length of correct, 
			and every item in the correct list must exist in my list

			correction: when Eleventy parses the query string, q2=X&q2=Y, we get: X, Y (see the space)?
			*/
			if(myanswer !== '') {
				let myanswers = myanswer.split(',').map(a => parseInt(a.trim(),10)-1);

				if(myanswers.length === question.correctAnswer.length) {
					let good = true;
					question.correctAnswer.forEach(ca => {
						if(myanswers.indexOf(ca) === -1) good = false;
					});

					if(good) correct++;
				}
			} 
		} else if(question.type === "truefalse") {
			// change my string bool to a real one
			myanswer = (myanswer === 'true');
			if(question.correctAnswer == myanswer) correct++;
		}
	});

	/*
	So for now, we just return an object of total questions and your result. I will also do the percentage
	for you.
	*/
	let result = {
		quizName: quiz.name,
		correct, 
		totalQuestions: quiz.questions.length,
		percentage: parseInt(correct/quiz.questions.length * 100,10)
	};

	return result;
});

Sorgu dizesinde sağlanan testin adını alarak ve verilerimde test “nesnesini” bularak başlıyorum. Sonuçları bu şekilde değerlendireceğim.

Buna sahip olduğumda, sonucu kontrol etme şeklim soru tipine dayanıyor. Bir kişinin yapması mümkün olduğundan olumsuzluk bir soruyu yanıtlayın, yanıtlarını varsayılan olarak boş bir dizeye alıyorum ve ardından sorgu dizesinde kontrol ediyorum. Çoğunlukla işe yarıyor, ancak “çoklu” şubeye bakarsanız, üzerinde biraz manipülasyon yapmam gerektiğini görürsünüz.

“Tek” ve “doğru/yanlış” soruları için sonucu kontrol etmek içindir. “Birden fazla” için, seçtiğinizden emin olmalıyım her cevap gerekli.

Kaç tanesini doğru yaptığına dair bir çetele tutarım ve sonra sonuçları veririm. İşte örnek bir sonuç sayfası:

Örnek sınav sonucu

Sarmak

Bunu çalışırken görmek isterseniz, buradan ziyaret edebilirsiniz: https://eleventyquiz.netlify.app/. Kaynak burada bulunabilir: https://github.com/cfjedimaster/eleventy-demos/tree/master/quiz.

Bu blog yazısı üzerinde çalışırken, kodumu nasıl kurduğumla ilgili ilginç bir sorunla karşılaştım. Makinemde her şey mükemmel çalıştı ve üretimde sınavlarımı görebiliyordum, ancak onları gönderdiğimde bir hata aldım. _dataquiz.js sınavların dizinini bulamadığını söylüyor. Gerçekten kafam karışmıştı (ve bunun hakkında Eleventy tartışma panosuna yazdım) burada), ancak düzeltme basitti – Eleventy Serverless’a dizini kopyalamasını söyleyin:

eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, {
	name: "serverless", 
	functionsDir: "./netlify/functions/",
	copy:['./quizzes']
});

Bu mantıklı gelmiyorsa, bana bildirin!

fotoğrafı çeken Nguyen Dang Hoang Nhu üzerinde Sıçramayı kaldır

İlgili Makaleler

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.

Başa dön tuşu