quick_exam 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6d26400fe1fe860ec5752c76d8cab490bda275bd0e6428df7ee568e02e579991
4
+ data.tar.gz: 97f74ccd29cc1f108376887666c6aafe943356a97d7c31a29d5478080fad28d9
5
+ SHA512:
6
+ metadata.gz: ed2e374cc26f5c17dd8b7063d1fe7efe1eee5923f165f29db38b167ac3d9a777d587a5d2036541a2e5d0243a77969b1ffe6aa814d35e5863f5d540cafb47a5b5
7
+ data.tar.gz: be53d3d2bb983648b096ac7ac19fa6198103ba420c38ae9f3c38bb2bdd4a4c5e176cab14a72fc44811349302655a52937dec48d55b350bea4108a4a92a51c844
@@ -0,0 +1,15 @@
1
+ .DS_Store
2
+
3
+ /.bundle/
4
+ /.yardoc
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ Gemfile.lock
12
+ *.gem
13
+ */quick_exam_export/
14
+ *.fld
15
+ ~*_sample.*
@@ -0,0 +1 @@
1
+ 2.4.0
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ The changelog is tracked here but also in the [Releases section of the GitHub project](https://github.com/rubykachu/quick_exam/releases). The changelog only includes changes specific to the RubyGem.
4
+
5
+ ## v1.0.0
6
+
7
+ - First release version `v1.0.0`
8
+ - Support file txt, docx and Webpage html from Word
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at minh.tang@tomosia.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in quick_exam.gemspec
4
+ gemspec
5
+
6
+ gem 'nokogiri'
7
+ gem 'sanitize', '4.6.6'
8
+ gem 'docx', '0.5.0'
9
+
10
+ group :development do
11
+ gem "rake", "~> 12.0"
12
+ gem 'pry'
13
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Shota Fukumori (sora_h)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,157 @@
1
+ # QuickExam
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/quick_exam.svg)](https://badge.fury.io/rb/quick_exam)
4
+
5
+ You can shuffle or randomize quiz questions and answers. Shuffling is also an effective way of preventing cheating because no two learners get questions in the same order while taking the same quiz.
6
+
7
+ The gem `quick_exam` **only supports txt, docx and Webpage html from Word** format. You can completely change the format of the questions and answers.
8
+
9
+
10
+ | Description | Example |
11
+ |-------------|------------------------|
12
+ | Format for a Question: Not case sensitive. The starting must be letters | `Q`, `Question` or `Câu` etc... |
13
+ | Format for an Answer: Not case sensitive. Starting with a letter or a number | `A`, `a` or `1`, `2` etc... |
14
+ | Format for an Correct answer: Not case sensitive | `!!!T` |
15
+
16
+ **Sample data**
17
+ ```
18
+ Q1. Who are all ________ people?
19
+ A. this
20
+ B. those !!!T
21
+ C. them
22
+ D. that
23
+
24
+ Q2. Claude is ________.
25
+ A. Frenchman
26
+ B. a French !!!T
27
+ C. a Frenchman
28
+ D. French man
29
+
30
+ Q3. I ____ a car next year.
31
+ A. buy
32
+ B. am buying !!!T
33
+ C. going to buy
34
+ D. bought
35
+ ```
36
+
37
+ ## Installation
38
+
39
+ Install the latest release yourself as:
40
+
41
+ ```bash
42
+ $ gem install quick_exam
43
+ ```
44
+
45
+ In Rails or others, add it to your Gemfile:
46
+
47
+ ```ruby
48
+ gem 'quick_exam'
49
+ ```
50
+
51
+ And then execute
52
+
53
+ ```bash
54
+ $ bundle install
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ### From CLI to export shuffle or randomize quiz question and answers
60
+
61
+ Run command help
62
+
63
+ ```bash
64
+ $ quick_exam help export
65
+ ```
66
+ to show options:
67
+
68
+ ```
69
+ Usage:
70
+ quick_exam export FILE_PATH [options]
71
+
72
+ Options:
73
+ -q, [--shuffle-question=true|false] # Shuffle the question
74
+ # Default: true
75
+
76
+ -a, [--shuffle-answer=true|false] # Shuffle the answer
77
+ # Default: false
78
+
79
+ -s, [--same-answer=false] # The same answer for all questionnaires
80
+ # Default: false
81
+
82
+ -Q, [--f-ques=Q] # Question format. Just prefix the question
83
+ # Default: Q
84
+
85
+ -C, [--f-corr=!!!T] # Correct answer format
86
+ # Default: !!!T
87
+
88
+ -c, [--count=2] # Number of copies to created
89
+ # Default: 2
90
+
91
+ -d, [--dest=~/quick_exam_export/] # File storage path after export
92
+
93
+ shuffled questions and answers then export file
94
+ ```
95
+
96
+ ### From script ruby to process raw data and get results after analysis
97
+
98
+
99
+ ```ruby
100
+ analyzer = QuickExam::Analyzer.new(path_file, f_ques: '', f_corr: '')
101
+ analyzer.analyze
102
+ ```
103
+
104
+ The object `analyzer` will have methods:
105
+
106
+ ```bash
107
+ .records # Return the data after analyzing
108
+ .total_line # Return total line of the file
109
+ .f_ques # Return the format question
110
+ .f_corr # Return the format correct answer
111
+ ```
112
+
113
+ To shuffle quiz question and answers
114
+
115
+ ```ruby
116
+ analyzer.records.mixes(count, shuffle_question: true, shuffle_answer: false, same_answer: false)
117
+ ```
118
+
119
+ The method `mixes` have 3 arguments:
120
+
121
+ ```bash
122
+ count: # Require. Number of copies to created
123
+ shuffle_question: # Optional. Shuffle quiz question. Default: true
124
+ shuffle_answer: # Optional. Shuffle answer. Default: false
125
+ same_answer: # Optional. Same answer for all questionnaires. Default: false
126
+ ```
127
+
128
+ ## Trick: Keep format Word
129
+
130
+ _Commonly used for mathematical or chemical formats_
131
+
132
+ `From Word > Save As > Save with format Webpage html`
133
+
134
+ **Step 1:**
135
+ ![image](https://user-images.githubusercontent.com/26104119/93707761-8a5a7080-fb5b-11ea-9df5-ca34a603ab4b.png)
136
+
137
+ **Step 2:**
138
+ ![image](https://user-images.githubusercontent.com/26104119/93707825-e1f8dc00-fb5b-11ea-8518-af07415ba4f3.png)
139
+
140
+ ## Development
141
+
142
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
143
+
144
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
145
+
146
+ ## Contributing
147
+
148
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tm-minhtang/quick_exam/issues. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected
149
+
150
+ 1. Fork it
151
+ 2. Create your feature branch (git checkout -b my-new-feature)
152
+ 3. Commit your changes (git commit -am 'Added some feature')
153
+ 4. Push to the branch (git push origin my-new-feature)
154
+ 5. Create new Pull Request
155
+
156
+ ---
157
+ _Authors: Tang Quoc Minh [vhquocminhit@gmail.com]_
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "quick_exam"
5
+
6
+ def reload!(print = true)
7
+ puts 'Reloading ...' if print
8
+ root_dir = File.expand_path('..', __dir__)
9
+ reload_dirs = %w{lib}
10
+ Object.send(:remove_const, :"QuickExam")
11
+ reload_dirs.each do |dir|
12
+ Dir.glob("#{root_dir}/#{dir}/**/**/*.rb").reverse.each { |f| load(f) }
13
+ end
14
+ true
15
+ end
16
+
17
+ begin
18
+ require 'pry'
19
+ Pry.start
20
+ rescue LoadError
21
+ require "irb"
22
+ IRB.start(__FILE__)
23
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
3
+ require 'quick_exam/cli'
4
+
5
+ # silence deprecations warning
6
+ ENV['THOR_SILENCE_DEPRECATION'] = '0'
7
+
8
+ QuickExam::CLI.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ require 'nokogiri'
2
+ require 'sanitize'
3
+ require "quick_exam/version"
4
+ require "quick_exam/core_ext"
5
+ require "quick_exam/handle_error"
6
+ require "quick_exam/analyzer"
7
+ require "quick_exam/export"
8
+
9
+ module QuickExam
10
+ # todo something
11
+ end
@@ -0,0 +1,57 @@
1
+ require 'docx'
2
+ require 'quick_exam/format'
3
+ require 'quick_exam/record'
4
+ require 'quick_exam/record_collection'
5
+ require 'quick_exam/analyst/common'
6
+
7
+ module QuickExam
8
+ module Analyst
9
+ class BaseDocx
10
+ include QuickExam::Format
11
+ attr_reader :records, :total_line
12
+
13
+ def initialize(file_path, f_ques:'' , f_corr:'')
14
+ raise ErrorAnalyze.new('No such file') unless File.exist? file_path
15
+ @file_path = file_path
16
+ @f_ques = f_ques
17
+ @f_corr = f_corr
18
+ @records = QuickExam::RecordCollection.new()
19
+ end
20
+
21
+ def analyze
22
+ data_standardize
23
+ self
24
+ rescue => e
25
+ raise ErrorAnalyze.new('Data can not analyze')
26
+ end
27
+
28
+ private
29
+
30
+ include QuickExam::Analyst::Common
31
+
32
+ def data_standardize
33
+ doc = Docx::Document.open(@file_path)
34
+ data = doc.paragraphs
35
+
36
+ @total_line = data.size
37
+ @object = QuickExam::Record.new()
38
+
39
+ data.each.with_index do |row, idx|
40
+ row = row.text
41
+ idx += 1 # The first row is 1
42
+
43
+ if end_of_line?(idx) || end_of_one_ticket_for_next_question?(row)
44
+ get_answer(row) # if the last line is answer then will get answer
45
+ collect_object_ticket
46
+ end
47
+
48
+ next if row.__blank?
49
+
50
+ next if get_answer(row)
51
+ next if get_question(row)
52
+ end
53
+ records
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,106 @@
1
+ require 'quick_exam/format'
2
+ require 'quick_exam/record'
3
+ require 'quick_exam/record_collection'
4
+
5
+ module QuickExam
6
+ class Analyst::BaseHTML
7
+ include QuickExam::Format
8
+ attr_reader :records, :total_line, :file
9
+
10
+ def initialize(file_path, f_ques: '' , f_corr: '')
11
+ raise ErrorAnalyze.new('No such file') unless File.exist? file_path
12
+ @file_path = file_path
13
+ @f_ques = f_ques
14
+ @f_corr = f_corr
15
+ @records = QuickExam::RecordCollection.new()
16
+ end
17
+
18
+ def analyze
19
+ data_standardize
20
+ end
21
+
22
+ private
23
+
24
+ include QuickExam::Analyst::Common
25
+
26
+ def data_standardize
27
+ @object = QuickExam::Record.new()
28
+
29
+ doc = File.open(@file_path) { |f| Nokogiri::HTML(f) }
30
+ data = doc.search('.WordSection1 p')
31
+
32
+ @total_line = data.size
33
+ data.each_with_index do |element, idx|
34
+ content_html = Sanitize.fragment(element, elements: ALLOWED_ELEMENTS).gsub(regex_ele_empty, '').__squish
35
+ idx += 1 # The first row is 1
36
+
37
+ if end_of_line?(idx) || end_of_one_ticket_for_next_question?(content_html)
38
+ get_answer_html(content_html) # if the last line is answer then will get answer
39
+ collect_object_ticket
40
+ end
41
+
42
+ next if element_empty? Sanitize.fragment(element)
43
+ next if get_answer_html(content_html)
44
+ next if get_question_html(content_html)
45
+ end
46
+ end
47
+
48
+ def get_answer_html(str)
49
+ return unless answer?(str)
50
+ ans = str.split(answer_mark_html(str)).last
51
+ return if ans.__blank?
52
+
53
+ ans = Nokogiri::HTML(ans).to_html
54
+ ans = Sanitize.fragment(ans, elements: ALLOWED_ELEMENTS)
55
+ ans = ans.split(@f_corr)[0].__squish
56
+
57
+ @object.answers << ans
58
+ get_correct_indexes_answer(str)
59
+ end
60
+
61
+ def get_question_html(str)
62
+ return if @object.answers.__present?
63
+ # Split question from question_mark and Get question
64
+ ques = str.split(question_mark_html(str)).last
65
+
66
+ # Chain question if not question and not answer
67
+ return @object.question += str if ques.__blank? && !answer?(str)
68
+
69
+ # fix unclosed HTML tags
70
+ ques = Nokogiri::HTML(ques).to_html
71
+ @object.question += Sanitize.fragment(ques, elements: ALLOWED_ELEMENTS).__squish
72
+ end
73
+
74
+ def question_mark_html(str)
75
+ dirty_f_ques = ''
76
+ str.chars.detect do |char|
77
+ dirty_f_ques += char
78
+ question?(dirty_f_ques)
79
+ end
80
+
81
+ # fix unclosed HTML tags
82
+ dirty_f_ques = Nokogiri::HTML(dirty_f_ques).to_html
83
+ Sanitize.fragment(dirty_f_ques, elements: ALLOWED_ELEMENTS).__squish
84
+ end
85
+
86
+ def answer_mark_html(str)
87
+ dirty_f_ans = ''
88
+ str.chars.detect do |char|
89
+ dirty_f_ans += char
90
+ answer?(dirty_f_ans)
91
+ end
92
+
93
+ # fix unclosed HTML tags
94
+ dirty_f_ans = Nokogiri::HTML(dirty_f_ans).to_html
95
+ Sanitize.fragment(dirty_f_ans, elements: ALLOWED_ELEMENTS).__squish
96
+ end
97
+
98
+ def element_empty?(str)
99
+ str.__squish.__blank? || (str =~ regex_ele_empty).__present?
100
+ end
101
+
102
+ def regex_ele_empty
103
+ /<[^\/>][^>]*>[\s]*<\/[^>]+>/
104
+ end
105
+ end
106
+ end