{"id":1180,"date":"2025-05-12T14:38:53","date_gmt":"2025-05-12T11:38:53","guid":{"rendered":"https:\/\/www.juustila.com\/antti\/?p=1180"},"modified":"2025-05-15T11:17:09","modified_gmt":"2025-05-15T08:17:09","slug":"multiple-git-repository-status-awareness-grading-app-updates","status":"publish","type":"post","link":"https:\/\/www.juustila.com\/antti\/2025\/05\/12\/multiple-git-repository-status-awareness-grading-app-updates\/","title":{"rendered":"Multiple Git repository status awareness &amp; grading app &#8211; updates"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In the <a href=\"https:\/\/www.juustila.com\/antti\/2025\/05\/06\/long-break-status-update-demo\/\">post last week<\/a> I mentioned that I&#8217;ve added new features to the <a href=\"https:\/\/www.juustila.com\/antti\/2023\/10\/14\/multiple-git-repository-status-awareness-app\/\">tool I use<\/a> in <strong>Data Structures and Algorithms<\/strong> course to supervise, support, test and grade the student programming course work. I&#8217;ll describe here the new features and provide some (hopefully) interesting implementation details about those.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Background about the course that helps in understand the role of this tool:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>students each have their own github or gitlab repository to where they fork the initial status of their course work from the teacher repository,<\/li>\n\n\n\n<li>repository contains the task instructions, skeleton code to implement, unit tests code and other supporting code given ready for students,<\/li>\n\n\n\n<li>repository tests enable testing the student&#8217;s implementation of the data structures and algorithms that are required to implement in the course,<\/li>\n\n\n\n<li>teachers have access to the student repositories to assist students with any issues, and finally to grade the work.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Before going into details, a comment about the <strong>motivation<\/strong> for this tool to exist in the first place: The course the tool is used in, has 300+ enrolled students and only two full time teachers (sometimes we do have 1-2 assistants with small number of hours, like 50 hrs in total for the whole Autumn), simultaneously teaching 2-3 <em>other<\/em> 200-300 student courses. That totals around 900+ active students taught simultaneously, so providing individual support and attention to all is practically impossible, at least if no automation (and other support tools) is taken into use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So the critical thing in managing all this work is, that we do need automated tools to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>keep track of how the students are progressing in the course and help those who desperately need it, and after the last deadline, <\/li>\n\n\n\n<li>to evaluate and grade the students&#8217; programming submissions as fast as possible. <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The grading should be done in three weeks after the last deadline, so it cannot be done in time manually.  The aim is to use this tool during the course, trying to catch those students that are not doing OK, and then try to contact and help them in getting their coursework going and spotting any possible mistakes in their work so that they can correct their mistakes before the final deadline. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Students next Fall will receive the same unit tests and the same code analysis tools (in Java unit tests) they can also themselves use. However, this is not enough, especially if the students do not contact the teachers about their issues soon enough or at all. A problem that is too common, unfortunately.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">The new features added to the teachers&#8217; tool during this Winter and Spring are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Automatic <strong>source code analysis<\/strong>:\n<ul class=\"wp-block-list\">\n<li>results of the source code analysis are stored in a database table; repository  table has a column where problems found in code are flagged,<\/li>\n\n\n\n<li>tool checks for java imports that are not allowed, notifies about this and sets error flag in the database table,<\/li>\n\n\n\n<li>tool also uses the Antrl parser generator to check if method <em>implementations<\/em> in the source code violate any rules and if the code does not do the things it should do.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Remote repo checks<\/strong> for both github and gitlab:\n<ul class=\"wp-block-list\">\n<li>checks that remote is not public, the rule of the course,<\/li>\n\n\n\n<li>checks that remote has only allowed contributors.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Testing and grading enhancements<\/strong>\n<ul class=\"wp-block-list\">\n<li>tests are now grouped to grade level tests,<\/li>\n\n\n\n<li>when all tests in a grade group passes, grade is stored to repository table,<\/li>\n\n\n\n<li>grade tests can be executed in whichever order, since grade value is bit flag,<\/li>\n\n\n\n<li>test for a certain student repository can be disabled if they cause issues like tests never ending or taking way too much time,<\/li>\n\n\n\n<li>if git pull updates code, grade is set to zero and test results are cleared.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Status reporting<\/strong>\n<ul class=\"wp-block-list\">\n<li>the tool now generates a textual status report that can be sent to student by email, listing how test pass, if any forbidden things are used \/ things to use are not used, git statistics etc.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s take a closer look at the code analysis done by the tool in this post. The other features are left for future possible posts to discuss.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Analyzing<\/strong> if the <strong>source code<\/strong> uses &#8220;forbidden&#8221; imports was implemented with simple text parsing techniques. The tool goes through the Java source files in a certain directory where student code is located, reads the .java files and checks if the source uses imports that are not allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, when implementing a Stack data structure, student must not import the <code>java.util.Stack<\/code> and just use it instead of implementing their own stack from scratch, using an array as internal data structure.  Or when sorting an array, <code>java.util.Arrays<\/code> is not imported and <code>Arrays.sort<\/code> is not used, because student should have used their <em>own<\/em> implementation of the sorting algorithms.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These kinds of mistakes can be found by analyzing the <code>import<\/code>s in the student&#8217;s .java files. You cannot call <code>Arrays.sort<\/code> without having <code>import java.util.Arrays<\/code> in the .java file. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The tool first reads a <strong>rules file<\/strong> that contains the import rules for the course:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;allowedImports]\n*,import java.util.Comparator\n*,import java.util.function.Predicate\n*,import oy.interact.tira.\n*,import java.lang.Math;\n*,import java.util.concurrent.atomic.AtomicInteger;\nCodeWordsCounter,import java.io.\nCodeWordsCounter,import java.nio.\nSetImplementation,import java.util.Iterator<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For example, the <code>java.util.Comparator<\/code> may be used by the students in all of their .java files (<code>*<\/code>). The same goes with <code>java.util.function.Predicate<\/code> and all the files in package <code>oy.interact.tira<\/code> (the course source packages). The class <code>SetImplementation<\/code> may <em>also<\/em> import <code>java.util.Iterator<\/code>. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Et cetera. As you can see, this is an <em>allow list<\/em>, not a <em>deny list<\/em>. The imports that are allowed and can be used in the course are small in number, so it is easier to have an allow list than deny list.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reading the allow list and analyzing the source files was straightforward to implement by simple file handling and text parsing. First read in the rules file and parse the import rules from there, into this structure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fileprivate\nstruct SourceRequirementsSpecification {\n   var allowedImports: &#91;String: &#91;String]] = &#91;:]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The Swift <a href=\"https:\/\/developer.apple.com\/documentation\/swift\/dictionary\">dictionary<\/a> object <code>allowedImports<\/code> has the class  as the key (either <code>*<\/code> or the name of the class rule is about), and then the array of allowed imports the class may contain. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then the tool starts reading the student .java files and handles all lines beginning with <code>import <\/code> to check if that class may import that specific thing or not. If the .java file imports something that was not allowed, the tool database table (for the student&#8217;s repository) having a column with integer value, a specific bit in that number (a &#8220;flag&#8221;) is set to 1 (true). This indicates that the student&#8217;s code imported something it was supposed to <em>not<\/em> do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the data structure keeping track of these repository flags, if they are set or not set:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct Flags: OptionSet {\n   typealias RawValue = Int16\n   var rawValue: Int16\n\n   static let remoteFails = Flags(rawValue: 1 &lt;&lt; 0)\n   static let buildFails = Flags(rawValue: 1 &lt;&lt; 1)\n   static let usesForbiddenFeatures = Flags(rawValue: 1 &lt;&lt; 2)\n   static let repositoryIsPublic = Flags(rawValue: 1 &lt;&lt; 3)\n   static let repositoryHasOutsideCollaborators = Flags(rawValue: 1 &lt;&lt; 4)\n   static let repositoryTestingIsOnHold = Flags(rawValue: 1 &lt;&lt; 5)\t\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The Swift <code><a href=\"https:\/\/developer.apple.com\/documentation\/swift\/optionset\">OptionSet<\/a><\/code> is a type that presents a mathematical set interface to a bit set. So each flag is a single bit in the 16 bit integer, being either off (0) or on (1). So instead of having six boolean columns in the database table, I can have one 16 bit integer column to store 16 different flags. Most of those still available for future needs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The flag <code>usesForbiddenFeatures<\/code> is set in this case of student code using forbidden imports (or other features, discussed below). Other flags are used to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>track if the remote repository cannot be accessed at all (<code>remoteFails<\/code>) or is empty,<\/li>\n\n\n\n<li>compiling the code fails (<code>buildFails<\/code>), <\/li>\n\n\n\n<li>repository is public even though it must be private (<code>repositoryIsPublic<\/code>) and finally <\/li>\n\n\n\n<li>if the repository has other collaborators than the student owning the repo and the course teachers (<code>repositoryHasOutsideCollaborators<\/code>). <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">These are all issues that have been met in the course during the previous years. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Final flag <code>repositoryTestingIsOnHold<\/code> can be set manually from the tool user interface for those repositories that mess up the testing, either because the tests are stuck or take too much time and therefore should not be executed before fixed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That flag struct is a 16 bit integer, the datatype of the database column storing the flags in each student repository database table. If the code has issues related to these rules, the flag is set. When the git repository is updated from the remote, the user needs to run the code analysis again. If a violation of a rule was fixed, the flag is then reset to zero (false), if no other violations are found.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The imports checking from source code is not enough, though.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes students do mistakes in <em>method implementations<\/em> they are not supposed to do. Like when implementing a <em>recursive<\/em> binary search, they implement an <em>iterative<\/em> binary search. Or when sorting a table in descending order, they first sort in ascending order and then reverse the table order. Since time efficiency is the major topic of the course, this is not what they are supposed to do. Instead, they should use a <em>comparator<\/em> to sort the table to <em>descending<\/em> order in the first place, and then no reverse operation is needed at all, making the operation faster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For checking these kinds of issues the import rules are not enough since these kind of mistakes can be made without importing something. Therefore, the same <strong>rules file<\/strong> contains also rules like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;codeRules]\n# Algorithms.binarySearchRecursive must contain word binarySearchRecursive\nAlgorithms.binarySearchRecursive,must,binarySearchRecursive\n# Algorithms.binarySearchRecursive must not contain word while\nAlgorithms.binarySearchRecursive,mustnot,while\nAlgorithms.binarySearchIterative,must,while\nCodeWordsCounter.topCodeWords,must,Algorithms.fastSort\nCodeWordsCounter.topCodeWords,mustnot,Algorithms.reverse<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here lines beginning with <code>#<\/code> are comments and ignored by the tool. Illustrating the examples of mistakes above, the rule:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Algorithms.binarySearchRecursive,mustnot,while<\/code> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">specifies that the algorithm <code>binarySearchRecursive<\/code> in class <code>Algorithms<\/code> <em>must not<\/em> contain the keyword <code>while<\/code>. Since breaking this rule implies that the student has implemented the <em>recursive<\/em> algorithm using <em>iterative<\/em> approach. Therefore, the learning goal of implementing a recursive binary search has not been met, and code needs to be fixed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These rules are also parsed from the rules file into the above mentioned rules structure, which now looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>enum Rule {\n   case must\n   case mustNot\n}\n\nstruct CodeRule {\n   let methodName: String\n   let rule: Rule\n   let stringToMatch: String\n}\n\nfileprivate\nstruct SourceRequirementsSpecification {\n   var allowedImports: &#91;String: &#91;String]] = &#91;:]\n   var rules: &#91;String: &#91;CodeRule]] = &#91;:] \/\/ ClassName : CodeRules\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In addition to the <code>allowedImports<\/code> rules, the structure now contains also <code>rules<\/code> for a class (the Swift dictionary key <code>String<\/code> has the class name), where the <code>CodeRule<\/code> structure has the name of the method the rule is about, then if the method must or must not (<code>Rule<\/code>) contain the string to match. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">This is a very simple current implementation, and as I develop this further, it may change to something different later. For example, it should be possible to say that the recursive binary search algorithm must not contain &#8220;while&#8221; but also not the &#8220;for&#8221; keyword.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">The second example about being able to realize that descending order can be implemented by a specific comparator (instead of ascending sort and then doing reverse, which is more time-consuming operation) is checked by the rule that the class <code>CodeWordsCounter<\/code> in the algorithm <code>topCodeWords<\/code> (where the work in the coding task is done) <em>must not<\/em> call <code>Algorithms.reverse<\/code>. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">If a student uses the Java <code>Collections.reverse<\/code> or Apache Commons <code>ArrayUtils.reverse<\/code> for this, that mistake is already caught using the import rules.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">For this feature implementation, I used the <a href=\"https:\/\/www.antlr.org\">Antrl parser generator<\/a>. Antlr has existing grammars for many languages, including Java, and it supports generating parsers also for Swift. So all I needed to do was to get the Antlr tool and the Java grammar files (<code>Java20Lexer.g4<\/code> and <code>Java20Parser.g4<\/code>) and then generate the Java parser and lexer for Swift:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ antlr -Dlanguage=Swift -message-format gnu -o Autogen Java20Lexer.g4\n$ antlr -Dlanguage=Swift -message-format gnu -o Autogen Java20Parser.g4<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Those commands place the generated Swift source code files in a subdirectory named <code>Autogen<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Using the generated Swift code from <code>Autogen<\/code>, the tool can then parse Java code. All I needed was to implement a class that extends the Antlr generated <code>Java20ParserBaseListener<\/code>, give the rules to this class and start parsing the <code>source<\/code> string read from the student&#8217;s java file, containing the Java source code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import Antlr4\nclass Java20MethodBodyChecker: Java20ParserBaseListener {\n\n   private let file: String\n   private let rules: &#91;CodeRule]\n   private var currentMethod: String = \"\"\n   private var stream: ANTLRInputStream? = nil\n\n   var violations: &#91;String] = &#91;]\n\t\n   func start(source: String) throws {\n      stream = ANTLRInputStream(source)\n      if let stream {\n         let lexer = Java20Lexer(stream)\n         let parser = try Java20Parser(CommonTokenStream(lexer))\n         let parserTree = try parser.compilationUnit()\n         let walker = ParseTreeWalker()\n         try walker.walk(self, parserTree)\n      }\n   }\n\/\/ ...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Analysing the code happens in the overridden handler for parsing Java methods, <code>enterMethodDeclaration<\/code>. There, the implementation checks, using the <code>rules<\/code>,  if the Java code contains any keywords that should not be there or does not contain keywords that should be here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>override func enterMethodDeclaration(_ ctx: Java20Parser.MethodDeclarationContext) {\n if let identifier = ctx.methodHeader()?.methodDeclarator()?.identifier() {\n  currentMethod = identifier.getText()\n  let ruleSet = rules.filter( { $0.methodName == currentMethod } )\n  if !ruleSet.isEmpty {\n   if let body = ctx.methodBody() {\n    let bodyText = body.getText()\n    for rule in ruleSet {\n     switch rule.rule {\n      case .must:\n       if !bodyText.contains(rule.stringToMatch) {\n        violations.append(\"\\(file).\\(rule.methodName) ei k\u00e4yt\u00e4 \\(rule.stringToMatch), vaikka pit\u00e4isi\")\n       }\n      case .mustNot:\n       if bodyText.contains(rule.stringToMatch) {\n        violations.append(\"\\(file).\\(rule.methodName) k\u00e4ytt\u00e4\u00e4 \\(rule.stringToMatch),  vaikka ei pit\u00e4isi\")\n       }\n     }\n    }\n   }\n  }\n }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>violations<\/code> then contains all the things found to be against the rules.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This worked, <em>except for one thing<\/em>: if the code has <em>comments<\/em> that do\/do not have the specified keywords of the rules, the code analysis <em>did check those comments also<\/em>. Even though the comments do not influence the implementation and are therefore totally irrelevant. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So if the <em>comments<\/em> of recursive binary search algorithm contains the keyword <code>while<\/code>, the tool would report that as a violation of the rule! Or if the code contains a required keyword, but not in the actual code implementation but only in the comments, the analysis would be satisfied. End result: not satisfactory at all.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First I started to fix this by implementing code myself to remove the comments from the code in a String variable before the analysis, but soon I realized that the Antlr parsing tool should somehow already handle this. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What I did instead, was (after some searching on the internet) to modify the Antrl lexer file for Java so that it does not parse the comments at all, but <em>skips<\/em> them:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ AJU: COMMENT: '\/*' .*? '*\/' -&gt; channel(HIDDEN);\nCOMMENT : '\/*' .*? '*\/' -&gt; skip;\n\n\/\/ AJU: LINE_COMMENT: '\/\/' ~&#91;\\r\\n]* -&gt; channel(HIDDEN);\nLINE_COMMENT : '\/\/' ~&#91;\\r\\n]* -&gt; skip;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then I regenerated the Swift parser and lexer source files and added them to the tool project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Using the new lexer\/parsers, the parsed Java code does not anymore include comments and I do not have to consider them at all! This works perfectly. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Without asking anything about any &#8220;AI&#8221; tools out there. As usual, I do not touch them even with a long stick. I prefer to discover and learn myself, as long as the internet is not polluted with &#8220;AI&#8221; generated crap which makes everything impossible.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Below you can see the tool reporting about imports in a student repository, imports that should not be in the code, and that the code in <code>CodeWordsCounter.topCodeWords<\/code> does not call  <code>Algorithms.fastSort<\/code>, even though it should, based on the rules in the rules file.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45-1024x662.png\" alt=\"\" class=\"wp-image-1182\" srcset=\"https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45-1024x662.png 1024w, https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45-300x194.png 300w, https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45-768x496.png 768w, https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45-1536x992.png 1536w, https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45-1200x775.png 1200w, https:\/\/www.juustila.com\/antti\/wp-content\/uploads\/2025\/05\/Nayttokuva-2025-05-12-kello-12.37.45.png 1554w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, the first violation is because the student has misnamed the class  as <code>setImplementation<\/code>. In Java, classes (and therefore also class files) should always begin with capital letters and the name should be <code>SetImplementation<\/code> instead. So this is a mistake, but actually does not violate the import rule; the Set data structure implementation <em>may<\/em> use the Iterator. Therefore, when the rules are found to be violated, it often requires human rechecking to make sure no unfair judgments are made by the automation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Considering the 300 something students in the course, perhaps each of them making at least 3-5 mistakes the code analysis could find, this results in 900-1500 mistakes in total. For the teacher to comb through all the hundreds of lines of code <em>in each<\/em> of the student projects, repeatedly during the course, working on this &#8220;code quality control&#8221; task manually is very time consuming and therefore not a realistic task. Especially considering the available teaching resources mentioned in the beginning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hopefully the new features of this tool and the similar tests given to students in Java test code, improve the support the teachers can provide during the course to lead the students to the correct path of implementing the required data structures and algorithms.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The post is getting quite long, so the rest of the new features will perhaps be discussed in future posts. Unless I decide to focus on other more interesting topics. Have a nice day!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the post last week I mentioned that I&#8217;ve added new features to the tool I use in Data Structures and Algorithms course to supervise, support, test and grade the student programming course work. I&#8217;ll describe here the new features and provide some (hopefully) interesting implementation details about those. Background about the course that helps &hellip; <a href=\"https:\/\/www.juustila.com\/antti\/2025\/05\/12\/multiple-git-repository-status-awareness-grading-app-updates\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Multiple Git repository status awareness &amp; grading app &#8211; updates&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[2],"tags":[48,174,178,173,129,19,77,177,175,70,46,12,15],"class_list":["post-1180","post","type-post","status-publish","format-standard","hentry","category-coding","tag-algorithms","tag-antlr","tag-bitflag","tag-code-analysis","tag-database","tag-git","tag-java","tag-lexing","tag-parsing","tag-programming","tag-swift","tag-teaching","tag-testing"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/posts\/1180","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/comments?post=1180"}],"version-history":[{"count":6,"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/posts\/1180\/revisions"}],"predecessor-version":[{"id":1197,"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/posts\/1180\/revisions\/1197"}],"wp:attachment":[{"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/media?parent=1180"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/categories?post=1180"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.juustila.com\/antti\/wp-json\/wp\/v2\/tags?post=1180"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}