perl cgi script for amino acids.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
5.9 KiB

2 years ago
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use Data::Dumper;
  5. # utility functions
  6. sub urldecode {
  7. my ($a) = @_;
  8. $a =~ s/\+/ /g;
  9. $a =~ s/%([A-Fa-f\d]{2})/chr hex $1/eg;
  10. return $a;
  11. }
  12. sub urlencode {
  13. my ($a) = @_;
  14. $a =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
  15. return $a;
  16. }
  17. sub parse_post() {
  18. my $POST_STRING = <>;
  19. my @POST = split(/[&=]/, $POST_STRING);
  20. my %POST_PARAMETERS = ();
  21. for (my $i = 0; $i < $#POST; $i += 2) {
  22. if (!defined $POST_PARAMETERS{$POST[$i]}) {
  23. $POST_PARAMETERS{$POST[$i]} = $POST[$i+1];
  24. } else {
  25. $POST_PARAMETERS{$POST[$i]} = $POST_PARAMETERS{$POST[$i]} . "," . urldecode ($POST[$i+1]);
  26. }
  27. }
  28. return \%POST_PARAMETERS;
  29. }
  30. sub check_equivalence {
  31. my ($string1,$string2) = @_;
  32. $string1 = (urldecode $string1) =~ s/^\s+|\s+$//rg;
  33. $string2 = (urldecode $string2) =~ s/^\s+|\s+$//rg;
  34. return $string1 =~ /^\s*$string2\s*$/i
  35. }
  36. print "Content-Type: text/html\n\n";
  37. print "<!DOCTYPE html>";
  38. print "<html>";
  39. print <<EOF;
  40. <head>
  41. <title>Amino Acid Practice</title>
  42. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  43. <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
  44. <style>
  45. .green {
  46. color: green;
  47. }
  48. .red {
  49. color: red;
  50. }
  51. </style>
  52. </head>
  53. <body>
  54. EOF
  55. print "<h1> Amino Acids </h1>";
  56. print "<article>";
  57. our @amino = (
  58. ["Phenylalanine", "Phe", "F", "F.png", "Nonpolar" ],
  59. ["Leucine", "Leu", "L", "L.png", "Nonpolar" ],
  60. ["Isoleucine", "Ile", "I", "I.png", "Nonpolar" ],
  61. ["Methionine", "Met", "M", "M.png", "Nonpolar" ],
  62. ["Valine", "Val", "V", "V.png", "Nonpolar" ],
  63. ["Proline", "Pro", "P", "P.png", "Nonpolar" ],
  64. ["Alanine", "Ala", "A", "A.png", "Nonpolar" ],
  65. ["Tryptophan", "Trp", "W", "W.png", "Nonpolar" ],
  66. ["Glycine", "Gly", "G", "G.png", "Nonpolar" ],
  67. ["Serine", "Ser", "S", "S.png", "Polar" ],
  68. ["Threonine", "Thr", "T", "T.png", "Polar" ],
  69. ["Tyrosine", "Tyr", "Y", "Y.png", "Polar" ],
  70. ["Glutamine", "Gln", "Q", "Q.png", "Polar" ],
  71. ["Asparagine", "Asn", "N", "N.png", "Polar" ],
  72. ["Cysteine", "Cys", "C", "C.png", "Polar" ],
  73. ["Aspartate", "Asp", "D", "D.png", "Acidic" ],
  74. ["Glutamate", "Glu", "E", "E.png", "Acidic" ],
  75. ["Histidine", "His", "H", "H.png", "Basic" ],
  76. ["Lysine", "Lys", "K", "K.png", "Basic" ],
  77. ["Arginine", "Arg", "R", "R.png", "Basic" ]
  78. );
  79. our @abbreviations = ("full name", "3-letter abbreviation", "1-letter abbreviation", "image");
  80. our @properties = ("Polar", "Nonpolar", "Acidic", "Basic");
  81. sub answer_choices {
  82. my %found = (); for(0..($#amino-1)) {$found{$_} = 1;};
  83. my @res;
  84. for((1..5)) {
  85. my $choice;
  86. do {
  87. $choice = int(rand $#amino);
  88. } while ($found{$choice} == 0);
  89. $found{$choice} = 0;
  90. push @res, $choice;
  91. }
  92. my $correct = $res[int(rand $#res)];
  93. return \@res, $correct;
  94. }
  95. sub to_image {
  96. my ($query) = @_;
  97. return $query =~ s/([^\s]*?).png/<img src='.\/images\/$1.png'>/r;
  98. }
  99. sub decode_question {
  100. my ($choices_idx, $correct_idx) = answer_choices();
  101. # generate a question type
  102. my %type_hash = (0 => 1, 1 => 1, 2 => 1, 3 => 1);
  103. my @types = ();
  104. for (1..2) {my $cur; do {$cur = int(rand 4);} while($type_hash{$cur} == 0); push @types, $cur; $type_hash{$cur} = 0;};
  105. my $question = "What is the " . $abbreviations[$types[1]] . " for " . $amino[$correct_idx]->[$types[0]] . "?";
  106. my $answer = $amino[$correct_idx]->[$types[1]];
  107. my @options = (); for(@$choices_idx) { push @options, $amino[$_]->[$types[1]] }
  108. if (rand() < 0.3 && $types[1] != 3) {
  109. return $question, $answer, [];
  110. }
  111. return $question, $answer, \@options;
  112. }
  113. sub properties_question {
  114. my ($choices_idx, $correct_idx) = answer_choices();
  115. my $question = "Property of the amino acid " . $amino[$correct_idx]->[int(rand 4)];
  116. return $question, $amino[$correct_idx]->[4], \@properties;
  117. }
  118. sub construct_form {
  119. my ($question, $answer, $options) = @_;
  120. $answer = $answer;
  121. $question = urldecode $question;
  122. my $question_label = to_image $question;
  123. print "<form action='' method='POST'>";
  124. print "<label for='response'>" . $question_label . "</label><br>";
  125. print "<input type='hidden' name='question' value='$question'>";
  126. if (scalar @$options) {
  127. print "<div>";
  128. for (my $i = 0; $i < scalar @$options; $i++) {
  129. my $opt = $options->[$i];
  130. my $lbl = to_image($options->[$i]);
  131. print "<input type='radio' name='response' value='$opt' id='$i'>";
  132. print "<label for='$i'>$lbl</label>";
  133. print "<input type='hidden' name='options' value=\"$opt\">";
  134. print "<br>";
  135. }
  136. print "</div>";
  137. } else {
  138. print "<input name='response' type='text'>";
  139. }
  140. print "<input type='hidden' name='answer' value='$answer'>";
  141. print "<br><input type='submit'/>";
  142. print "</form>";
  143. }
  144. if ($ENV{'REQUEST_METHOD'} =~ /POST/i) {
  145. my $parameters = parse_post();
  146. our $response = ($parameters->{'response'} // '');
  147. our $answer = ($parameters->{'answer'} // 'wrong');
  148. our $question = ($parameters->{'question'} // '');
  149. our @options = split(',', $parameters->{'options'} // '');
  150. # answer and response should have the same elements.
  151. our $correct = check_equivalence $answer, $response;
  152. construct_form($question, $answer, \@options);
  153. if ($correct) {
  154. print "<p class='green'>Correct!</p>";
  155. } else {
  156. print "<p class='red'> Incorrect! </p>";
  157. }
  158. } else {
  159. if (int(rand(2)) == 0) {
  160. construct_form(properties_question());
  161. } else {
  162. construct_form(decode_question());
  163. }
  164. }
  165. print "<hr>";
  166. print "<button onclick='window.location.href = window.location.href'>Next</button>";
  167. print "</article>";
  168. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  169. $year += 1900;
  170. print "<p>&copy; Juni Kim 2022-$year</p>";
  171. print"</body></html>";