diff --git a/.github/workflows/auto-merge-dependency-updates.yaml b/.github/workflows/auto-merge-dependency-updates.yaml index 5451a5093..d569427b1 100644 --- a/.github/workflows/auto-merge-dependency-updates.yaml +++ b/.github/workflows/auto-merge-dependency-updates.yaml @@ -13,7 +13,7 @@ jobs: steps: - name: Dependabot metadata id: metadata - uses: dependabot/fetch-metadata@v1.3.4 + uses: dependabot/fetch-metadata@v1.3.5 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Approve diff --git a/.github/workflows/crowdin-actions.yaml b/.github/workflows/crowdin-actions.yaml index 4b19df1cf..90830f5c9 100644 --- a/.github/workflows/crowdin-actions.yaml +++ b/.github/workflows/crowdin-actions.yaml @@ -19,12 +19,12 @@ jobs: uses: actions/checkout@v3 - name: Upload or update source files to Crowdin - uses: crowdin/github-action@1.5.0 + uses: crowdin/github-action@1.5.2 with: upload_sources: true - name: Download German translations - uses: crowdin/github-action@1.5.0 + uses: crowdin/github-action@1.5.2 with: upload_sources: false download_translations: true @@ -42,7 +42,7 @@ jobs: config: crowdin.yaml - name: Download Spanish translations - uses: crowdin/github-action@1.5.0 + uses: crowdin/github-action@1.5.2 with: upload_sources: false download_translations: true diff --git a/.github/workflows/test-and-deploy.yaml b/.github/workflows/test-and-deploy.yaml index 3de79555d..4ae0c5d4e 100644 --- a/.github/workflows/test-and-deploy.yaml +++ b/.github/workflows/test-and-deploy.yaml @@ -19,6 +19,9 @@ jobs: uses: actions/checkout@v3 - name: Create Docker network run: docker network create uccser-development-stack + # Required for the node service + - name: Set DOCKER_UID variable + run: echo "DOCKER_UID=$(echo $UID)" >> $GITHUB_ENV - name: Start systems run: docker compose -f docker-compose.local.yml up -d - name: Run Django system check @@ -32,6 +35,9 @@ jobs: uses: actions/checkout@v3 - name: Create Docker network run: docker network create uccser-development-stack + # Required for the node service + - name: Set DOCKER_UID variable + run: echo "DOCKER_UID=$(echo $UID)" >> $GITHUB_ENV - name: Start systems run: docker compose -f docker-compose.local.yml up -d - name: Create static files @@ -125,6 +131,9 @@ jobs: - name: Create Docker network run: docker network create uccser-development-stack + # Required for the node service + - name: Set DOCKER_UID variable + run: echo "DOCKER_UID=$(echo $UID)" >> $GITHUB_ENV - name: Start system run: docker compose -f docker-compose.local.yml up -d @@ -174,6 +183,10 @@ jobs: - name: Create Docker network run: docker network create uccser-development-stack + # Required for the node service + - name: Set DOCKER_UID variable + run: echo "DOCKER_UID=$(echo $UID)" >> $GITHUB_ENV + - name: Start system run: docker compose -f docker-compose.local.yml up -d diff --git a/csfieldguide/chapters/content/en/coding-compression/sections/shannons-experiment.md b/csfieldguide/chapters/content/en/coding-compression/sections/shannons-experiment.md new file mode 100644 index 000000000..a8e346cc3 --- /dev/null +++ b/csfieldguide/chapters/content/en/coding-compression/sections/shannons-experiment.md @@ -0,0 +1,62 @@ +# Shannon's experiment + +It turns out that there are limits to how small we can compress a file, and to explore this we’re going to look at multi-million dollar frauds, and a fun game that exposes the limits of compression. + +Every so often someone claims to have invented an amazing {glossary-link term="lossless"}lossless{glossary-link end} compression method that can compress *any* file, including compressed files. If that was true, it would mean that you could use the method to compress files down to just a few {glossary-link term="byte"}bytes{glossary-link end}. Any file could be downloaded in a fraction of a second, and computers could store billions of huge video files. It would revolutionise computing! But is there a limit on how small a file can be compressed? + +{panel type="curiosity"} + +# Fraud in data compression + +A number of fake systems have been produced that claim to compress any file as small as you want. +They have even been demonstrated! But they all turn out to be a fake system - the common trick is to have a “compression” program that actually just hides the file being compressed somewhere else on a computer, and replaces it with a tiny file. The decompression program copies the hidden file back. It’s a very simple program to write, and looks very impressive because files are replaced with tiny ones, and then reproduced exactly. + +You can find a few examples of these if you search for “Pixelon”, “Adam’s platform”, “Near Zero”, or “Madison Priest” (add the terms “compression” and “fraud” if you are searching for these, as there are other legitimate organisations with similar names). Several of these organisations have taken millions of dollars from investors who didn’t understand the limits of compression, and all ended up failing. + +{panel end} + +## How small can we compress a file? + +With {glossary-link term="lossy"}lossy{glossary-link end} compression, there isn’t a limit to how small you can compress a file, since it’s just a matter of giving up quality to make the file smaller. You could compress a 10-megapixel photo down to just one pixel (perhaps the average colour of the whole photo). It wouldn’t be much use to anyone, but technically it’s a lossy version of the original photo. + +But with lossless compression, the original file needs to be able to be restored to exactly its original form. + +We’ve seen that compression works by taking advantage of patterns in the data being compressed. +In the 1950s an interesting experiment was developed by a scientist called Claude Shannon, in which he asked humans to predict English text, and he measured how good the compression would be using their ability to make predictions. +The idea is that if a computer was as good at English as a human, then that might be near the limit of what is possible. + +Shannon’s game is easy to play. +Just click on the letter that you think is coming up next in the sentence (you’ll need to start by guessing the first letter). The number of guesses you make give an indication of how predictable the letter is. +These guesses are used to estimate how small the data could be compressed -- you can see this estimate by clicking on the “Show statistics” button. +The “bits per character” is the estimate of how many bits would be needed on average to represent each character. +Plain English text is often stored in 7 or 8 bits for each character (using Unicode or ASCII), and you should find that using your predictions the experiment can do better than that, usually around 2 bits per character. +That’s equivalent to compressing a normal file (8 bits per character) to a quarter of its size. + +Try it here: + +{interactive slug="shannon-experiment" type="whole-page" alt="Shannon's experiment"} + +But it’s very hard to get smaller than 1 bit per character (one eighth of the normal size). +Shannon found that this seems to be a limit for how much we can compress English text. +And this is one reason that we should be suspicious of any system that claims to compress English text to much smaller than one eighth of its original size. + +{panel type="teacher-note"} + +# Creating your own experiment + +This interactive contains an option to create your own experiment. +For example, this could be used to tailor the sentence set to use words that are more familiar to your students. + +Additionally we have support for multiple different languages and sentence sets. +Currently, we have a sentence set for Te Reo Māori, and the original sentences used by Shannon in 1951. + +If you would like to use another language with a different set of characters and/or accents, this also works! +When creating a custom sentence, any characters that aren't already on the keyboard get added automatically. + +Lastly, you could also considering using a pattern that is easily guessed once they realise what is happening, such as "AAAAAAAAAAAAAAAA", "ABABABABABAB", or "blah blah blah blah blah blah blah blah blah blah". +These have very close to zero information content as they are very predictable. +At the other extreme, a (fake) passowrd such as "P6dQKg#S58dw66p" could be used to explore how hard it is to guess random characters. + +{panel end} + +{comment - could add more about Shannon, model at sender and received, movie about him} diff --git a/csfieldguide/chapters/content/structure/coding-compression/sections/sections.yaml b/csfieldguide/chapters/content/structure/coding-compression/sections/sections.yaml index 3b724acc2..bc3ab99f2 100644 --- a/csfieldguide/chapters/content/structure/coding-compression/sections/sections.yaml +++ b/csfieldguide/chapters/content/structure/coding-compression/sections/sections.yaml @@ -19,8 +19,11 @@ general-purpose: audio-compression: section-number: 7 -the-whole-story: +shannons-experiment: section-number: 8 -further-reading: +the-whole-story: section-number: 9 + +further-reading: + section-number: 10 \ No newline at end of file diff --git a/csfieldguide/chapters/management/commands/_ChapterSectionHeadingsLoader.py b/csfieldguide/chapters/management/commands/_ChapterSectionHeadingsLoader.py index 68c8ae808..400f46d65 100644 --- a/csfieldguide/chapters/management/commands/_ChapterSectionHeadingsLoader.py +++ b/csfieldguide/chapters/management/commands/_ChapterSectionHeadingsLoader.py @@ -39,13 +39,16 @@ def load(self): """ for language, content in self.content_translations.items(): + i = 0 if content.heading_tree: - for (i, heading_node) in enumerate(content.heading_tree): + for heading_node in content.heading_tree: self.chapter_section.headings.update_or_create( - slug=heading_node.title_slug, + number=i, + language=language, defaults={ 'name': heading_node.title, - 'language': language, - 'number': i, + 'slug': heading_node.title_slug } ) + i += 1 + self.chapter_section.headings.filter(number__gte=i, language=language).delete() diff --git a/csfieldguide/chapters/management/commands/_ChapterSectionsLoader.py b/csfieldguide/chapters/management/commands/_ChapterSectionsLoader.py index 8ce0c0430..eb4998534 100644 --- a/csfieldguide/chapters/management/commands/_ChapterSectionsLoader.py +++ b/csfieldguide/chapters/management/commands/_ChapterSectionsLoader.py @@ -33,10 +33,9 @@ def load(self): field. """ chapter_sections_structure = self.load_yaml_file(self.structure_file_path) - section_numbers = [] + next_section_number = 1 for (section_slug, section_structure) in chapter_sections_structure.items(): - if section_structure is None: raise MissingRequiredFieldError( self.structure_file_path, @@ -57,8 +56,15 @@ def load(self): "section-number - value '{}' is invalid".format(section_number), "section-number must be an integer value." ) + if section_number != next_section_number: + raise InvalidYAMLValueError( + self.structure_file_path, + "section-number - value '{}' is invalid".format(section_number), + "section-numbers must be in sequential order. The next expected number was '{}'." + .format(next_section_number) + ) - section_numbers.append(section_number) + next_section_number += 1 chapter_section_translations = self.get_blank_translation_dictionary() @@ -69,9 +75,9 @@ def load(self): chapter_section_translations[language]["name"] = content.title chapter_section, created = self.chapter.chapter_sections.update_or_create( - slug=section_slug, + number=section_number, defaults={ - 'number': section_number, + 'slug': section_slug, 'languages': list(content_translations.keys()), } ) @@ -101,11 +107,4 @@ def load(self): structure_filename=self.structure_file_path, ).load() - # assumes first section number is always 1 - for counter, section_number in enumerate(section_numbers, 1): - if section_number != counter: - raise InvalidYAMLValueError( - self.structure_file_path, - "section-number - value '{}' is invalid".format(section_number), - "section-numbers must be in sequential order. The next expected number was '{}'.".format(counter) - ) + self.chapter.chapter_sections.filter(number__gte=next_section_number).delete() diff --git a/csfieldguide/chapters/management/commands/_ChaptersLoader.py b/csfieldguide/chapters/management/commands/_ChaptersLoader.py index de660e5aa..7d53e72fa 100644 --- a/csfieldguide/chapters/management/commands/_ChaptersLoader.py +++ b/csfieldguide/chapters/management/commands/_ChaptersLoader.py @@ -69,9 +69,9 @@ def load(self): # Create or update chapter object and save to the db chapter, created = Chapter.objects.update_or_create( - slug=self.chapter_slug, + number=self.chapter_number, defaults={ - 'number': self.chapter_number, + 'slug': self.chapter_slug, 'icon': chapter_icon, 'video': video } diff --git a/csfieldguide/chapters/management/commands/loadchapters.py b/csfieldguide/chapters/management/commands/loadchapters.py index c44e195e4..3b34fb022 100644 --- a/csfieldguide/chapters/management/commands/loadchapters.py +++ b/csfieldguide/chapters/management/commands/loadchapters.py @@ -3,6 +3,7 @@ import os.path from django.core.management.base import BaseCommand from django.conf import settings +from django.db import transaction from utils.BaseLoader import BaseLoader from utils.LoaderFactory import LoaderFactory from utils.errors.MissingRequiredFieldError import MissingRequiredFieldError @@ -19,6 +20,7 @@ class Command(BaseCommand): help = "Converts Markdown files listed in structure file and stores" + @transaction.atomic def handle(self, *args, **options): """Automatically called when the loadchapters command is given.""" factory = LoaderFactory() @@ -51,6 +53,7 @@ def handle(self, *args, **options): "Application Structure" ) else: + next_chapter_number = 1 for chapter_slug in chapters: chapter_structure_file = "{}.yaml".format(chapter_slug) @@ -67,6 +70,16 @@ def handle(self, *args, **options): "chapter-number - value '{}' is invalid".format(chapter_number), "chapter-number must be an integer value." ) + if chapter_number != next_chapter_number: + raise InvalidYAMLValueError( + structure_file_path, + "chapter-number - value '{}' is invalid".format(chapter_number), + ("chapter-numbers must be in sequential order. The next expected number was '{}'." + .format(next_chapter_number)) + ) + + next_chapter_number += 1 + factory.create_chapter_loader( base_path=base_path, content_path=chapter_slug, diff --git a/csfieldguide/chapters/migrations/0037_chapter_slug_deferred.py b/csfieldguide/chapters/migrations/0037_chapter_slug_deferred.py new file mode 100644 index 000000000..8c082e195 --- /dev/null +++ b/csfieldguide/chapters/migrations/0037_chapter_slug_deferred.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2022-11-24 02:49 + +from django.db import migrations, models +import django.db.models.constraints + + +class Migration(migrations.Migration): + + dependencies = [ + ('chapters', '0036_alter_chaptersection_options'), + ] + + operations = [ + migrations.AddConstraint( + model_name='chapter', + constraint=models.UniqueConstraint(deferrable=django.db.models.constraints.Deferrable['DEFERRED'], fields=('slug',), name='slug_deferred'), + ), + ] diff --git a/csfieldguide/chapters/migrations/0038_alter_chapter_slug.py b/csfieldguide/chapters/migrations/0038_alter_chapter_slug.py new file mode 100644 index 000000000..bbb3b7b04 --- /dev/null +++ b/csfieldguide/chapters/migrations/0038_alter_chapter_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2022-11-24 03:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('chapters', '0037_chapter_slug_deferred'), + ] + + operations = [ + migrations.AlterField( + model_name='chapter', + name='slug', + field=models.SlugField(), + ), + ] diff --git a/csfieldguide/chapters/models.py b/csfieldguide/chapters/models.py index 8d9b4d402..f35561cd5 100644 --- a/csfieldguide/chapters/models.py +++ b/csfieldguide/chapters/models.py @@ -2,7 +2,6 @@ from django.db import models from interactives.models import Interactive -from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from utils.TranslatableModel import TranslatableModel from django.urls import reverse @@ -54,7 +53,7 @@ class Chapter(TranslatableModel): """Model for chapter in database.""" # Auto-incrementing 'id' field is automatically set by Django - slug = models.SlugField(unique=True) + slug = models.SlugField() # This is set unique in the Meta child class name = models.CharField(max_length=100, default="") number = models.SmallIntegerField(unique=True) introduction = models.TextField(default="") @@ -104,6 +103,10 @@ class Meta: verbose_name = _("chapter") verbose_name_plural = _("chapters") + constraints = [ + models.UniqueConstraint(fields=["slug"], deferrable=models.Deferrable.DEFERRED, name="slug_deferred") + ] + class ChapterSection(TranslatableModel): """Model for each section in a chapter in database.""" @@ -128,24 +131,6 @@ def __str__(self): """ return self.name - def clean(self): - """Use to check for unique section numbers. - - Raises: - ValidationError: when the section being added uses - an existing section number for this chapter. - """ - # get all sections with same section number and chapter as new section being added - sections = ChapterSection.objects.filter(number=self.number, chapter=self.chapter) - # if already exists section with same number in same chapter, then throw error! - if len(sections) > 1: - raise ValidationError(('Section number must be unique per chapter.')) - - def save(self, *args, **kwargs): - """Override save method to validate unique section numbers.""" - super(ChapterSection, self).save(*args, **kwargs) - self.clean() - class Meta: """Set consistent ordering of chapter sections.""" diff --git a/csfieldguide/config/__init__.py b/csfieldguide/config/__init__.py index ed081da1a..856f65721 100644 --- a/csfieldguide/config/__init__.py +++ b/csfieldguide/config/__init__.py @@ -1,3 +1,3 @@ """Module for Django system configuration.""" -__version__ = "3.12.6" +__version__ = "3.13.0" diff --git a/csfieldguide/package-lock.json b/csfieldguide/package-lock.json index 4db067723..411e922fe 100644 --- a/csfieldguide/package-lock.json +++ b/csfieldguide/package-lock.json @@ -2439,9 +2439,9 @@ "dev": true }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true }, "decompress": { @@ -3049,9 +3049,9 @@ } }, "engine.io": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", - "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -6052,9 +6052,9 @@ } }, "jquery": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", - "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.2.tgz", + "integrity": "sha512-/e7ulNIEEYk1Z/l4X0vpxGt+B/dNsV8ghOPAWZaJs8pkGvsSC0tm33aMGylXcj/U7y4IcvwtMXPMyBFZn/gK9A==", "dev": true }, "js-yaml": { @@ -7516,9 +7516,9 @@ "dev": true }, "postcss": { - "version": "8.4.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", - "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", + "version": "8.4.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", + "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", "dev": true, "requires": { "nanoid": "^3.3.4", @@ -8538,9 +8538,9 @@ "dev": true }, "sass": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz", - "integrity": "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A==", + "version": "1.56.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.2.tgz", + "integrity": "sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -9006,9 +9006,9 @@ "dev": true }, "socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz", + "integrity": "sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig==", "dev": true, "requires": { "@types/component-emitter": "^1.2.10", @@ -9054,9 +9054,9 @@ } }, "socket.io-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.0.tgz", - "integrity": "sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", "dev": true, "requires": { "@socket.io/component-emitter": "~3.1.0", @@ -10452,9 +10452,9 @@ "dev": true }, "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dev": true, "requires": { "cliui": "^8.0.1", @@ -10463,7 +10463,7 @@ "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, "dependencies": { "ansi-regex": { diff --git a/csfieldguide/package.json b/csfieldguide/package.json index dcf175d8e..ede16ac86 100644 --- a/csfieldguide/package.json +++ b/csfieldguide/package.json @@ -30,16 +30,16 @@ "gulp-tap": "2.0.0", "gulp-terser": "2.1.0", "iframe-resizer": "4.3.2", - "jquery": "3.6.1", + "jquery": "3.6.2", "lity": "2.4.1", "multiple-select": "1.5.2", "pixrem": "5.0.0", "popper.js": "1.16.1", - "postcss": "8.4.18", + "postcss": "8.4.20", "postcss-flexbugs-fixes": "5.0.2", - "sass": "1.55.0", + "sass": "1.56.2", "vinyl-buffer": "1.0.1", - "yargs": "17.6.0" + "yargs": "17.6.2" }, "engines": { "node": ">=8" diff --git a/csfieldguide/static/interactives/scene-editor/package.json b/csfieldguide/static/interactives/scene-editor/package.json index 293cf4247..29f75cb73 100644 --- a/csfieldguide/static/interactives/scene-editor/package.json +++ b/csfieldguide/static/interactives/scene-editor/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "mathjs": "6.0.3", - "three": "0.146.0", + "three": "0.147.0", "three-orbit-controls": "82.1.0", "sprintf-js": "1.1.2" } diff --git a/csfieldguide/templates/appendices/contributors.html b/csfieldguide/templates/appendices/contributors.html index ca59d2cab..8505356f1 100644 --- a/csfieldguide/templates/appendices/contributors.html +++ b/csfieldguide/templates/appendices/contributors.html @@ -27,6 +27,7 @@

{% trans 'Software
  • Hayley van Waas
  • Courtney Bracefield
  • Alasdair Smith
  • +
  • Philip Dolbel
  • {% trans 'Interactive Development' %}

    diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/add-heading-middle/add-heading-middle.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/add-heading-middle/add-heading-middle.md new file mode 100644 index 000000000..399919e48 --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/add-heading-middle/add-heading-middle.md @@ -0,0 +1,9 @@ +# This is a section + +## This is a second level heading + +## This heading was added in the middle + +## This is another second level heading + +This is the content for the second section. diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/added-section/added-section.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/added-section/added-section.md new file mode 100644 index 000000000..c93570986 --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/added-section/added-section.md @@ -0,0 +1,3 @@ +# This is the added section heading + +This is the added section diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/added-section/single-section.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/added-section/single-section.md new file mode 100644 index 000000000..524f6f2bc --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/added-section/single-section.md @@ -0,0 +1,3 @@ +# This is the section heading + +This is the content for the section. diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/middle-section.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/middle-section.md new file mode 100644 index 000000000..6fe64721c --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/middle-section.md @@ -0,0 +1,3 @@ +# This is the middle section heading + +This is the content for the middle section diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/multiple-sections-section-1.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/multiple-sections-section-1.md new file mode 100644 index 000000000..d602c7e84 --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/multiple-sections-section-1.md @@ -0,0 +1,3 @@ +# This is the first section + +This is the content for the first section. diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/multiple-sections-section-2.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/multiple-sections-section-2.md new file mode 100644 index 000000000..5843e4d85 --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/middle-section/multiple-sections-section-2.md @@ -0,0 +1,3 @@ +# This is the second section + +This is the content for the second section. diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-1.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-1.md index d602c7e84..d8d0c4682 100644 --- a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-1.md +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-1.md @@ -1,3 +1,5 @@ # This is the first section +## This is a second level heading + This is the content for the first section. diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-2.md b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-2.md index 5843e4d85..1bbab9023 100644 --- a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-2.md +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/en/multiple-sections/multiple-sections-section-2.md @@ -1,3 +1,7 @@ # This is the second section +## This is a second level heading + +## This is another second level heading + This is the content for the second section. diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/add-heading-middle/add-heading-middle.yaml b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/add-heading-middle/add-heading-middle.yaml new file mode 100644 index 000000000..33ffe094c --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/add-heading-middle/add-heading-middle.yaml @@ -0,0 +1,2 @@ +add-heading-middle: + section-number: 1 diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/added-section/added-section.yaml b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/added-section/added-section.yaml new file mode 100644 index 000000000..0d803c96e --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/added-section/added-section.yaml @@ -0,0 +1,5 @@ +single-section: + section-number: 1 + +added-section: + section-number: 2 diff --git a/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/middle-section/middle-section.yaml b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/middle-section/middle-section.yaml new file mode 100644 index 000000000..389fb1d4c --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/assets/chapter-sections/structure/middle-section/middle-section.yaml @@ -0,0 +1,8 @@ +multiple-sections-section-1: + section-number: 1 + +middle-section: + section-number: 2 + +multiple-sections-section-2: + section-number: 3 diff --git a/csfieldguide/tests/chapters/loaders/test_chapter_section_headings_loader.py b/csfieldguide/tests/chapters/loaders/test_chapter_section_headings_loader.py new file mode 100644 index 000000000..147e70ad2 --- /dev/null +++ b/csfieldguide/tests/chapters/loaders/test_chapter_section_headings_loader.py @@ -0,0 +1,252 @@ +import os.path +from unittest import mock +from tests.BaseTestWithDB import BaseTestWithDB +from tests.chapters.ChaptersTestDataGenerator import ChaptersTestDataGenerator +from chapters.management.commands._ChapterSectionsLoader import ChapterSectionsLoader +from chapters.management.commands._ChapterSectionHeadingsLoader import ChapterSectionHeadingsLoader +from chapters.models import ChapterSection, ChapterSectionHeading + + +class ChapterSectionHeadingsLoaderTest(BaseTestWithDB): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.test_data = ChaptersTestDataGenerator() + self.loader_name = "chapter-sections" + self.base_path = os.path.join(self.test_data.LOADER_ASSET_PATH, self.loader_name) + + def test_chapters_chapter_section_loader_single_heading(self): + test_slug = "multiple-sections" + section_slug = "multiple-sections-section-1" + chapter = self.test_data.create_chapter(1) + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [""] + ) + + def test_chapters_chapter_section_loader_multiple_headings(self): + test_slug = "multiple-sections" + section_slug = "multiple-sections-section-2" + chapter = self.test_data.create_chapter(1) + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + "" + ] + ) + + def test_chapters_chapter_section_loader_added_heading(self): + test_slug = "multiple-sections" + section_slug = "multiple-sections-section-1" + chapter = self.test_data.create_chapter(1) + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + ] + ) + + # Now add the new heading while the previous one is in the database + section_slug = "multiple-sections-section-2" + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + "" + ] + ) + + def test_chapters_chapter_section_loader_added_middle(self): + test_slug = "multiple-sections" + section_slug = "multiple-sections-section-2" + chapter = self.test_data.create_chapter(1) + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + "" + ] + ) + + # Now add the new heading while the previous one is in the database + test_slug = "add-heading-middle" + section_slug = "add-heading-middle" + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + "", + "" + ] + ) + + def test_chapters_chapter_section_removed_heading(self): + test_slug = "multiple-sections" + section_slug = "multiple-sections-section-2" + chapter = self.test_data.create_chapter(1) + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + "" + ] + ) + + # Now add the new heading while the previous one is in the database + section_slug = "multiple-sections-section-1" + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + content_translations = chapter_section_loader.get_markdown_translations( + "{}.md".format(section_slug) + ) + chapter_section_heading_loader = ChapterSectionHeadingsLoader( + factory, + ChapterSection.objects.first(), + content_translations, + base_path=self.base_path, + ) + chapter_section_heading_loader.load() + self.assertQuerysetEqual( + ChapterSectionHeading.objects.all(), + [ + "", + ] + ) diff --git a/csfieldguide/tests/chapters/loaders/test_chapter_sections_loader.py b/csfieldguide/tests/chapters/loaders/test_chapter_sections_loader.py index 5aa523f45..6cd104ba3 100644 --- a/csfieldguide/tests/chapters/loaders/test_chapter_sections_loader.py +++ b/csfieldguide/tests/chapters/loaders/test_chapter_sections_loader.py @@ -1,6 +1,5 @@ import os.path from unittest import mock -from django.core.exceptions import ValidationError from tests.BaseTestWithDB import BaseTestWithDB from tests.chapters.ChaptersTestDataGenerator import ChaptersTestDataGenerator from tests.interactives.InteractivesTestDataGenerator import InteractivesTestDataGenerator @@ -118,7 +117,7 @@ def test_chapters_chapter_section_loader_duplicate_section_numbers(self): structure_filename="{}.yaml".format(test_slug), ) self.assertRaises( - ValidationError, + InvalidYAMLValueError, chapter_section_loader.load ) @@ -197,3 +196,117 @@ def test_chapters_chapter_section_loader_non_sequential_section_number(self): InvalidYAMLValueError, chapter_section_loader.load ) + + def test_chapters_chapter_section_loader_added_section(self): + test_slug = "single-section" + chapter = self.test_data.create_chapter("1") + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + self.assertQuerysetEqual( + ChapterSection.objects.all(), + [""] + ) + + # Now add the section once the previous one is in the database + test_slug = "added-section" + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + self.assertQuerysetEqual( + ChapterSection.objects.all(), + [ + "", + "" + ] + ) + + def test_chapters_chapter_section_loader_insert_middle_section(self): + test_slug = "multiple-sections" + chapter = self.test_data.create_chapter("1") + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + self.assertQuerysetEqual( + ChapterSection.objects.all(), + [ + "", + "" + ] + ) + + # Now add the section to the middle now that the previous + # ones are in the database + test_slug = "middle-section" + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + self.assertQuerysetEqual( + ChapterSection.objects.all(), + [ + "", + "", + "" + ] + ) + + def test_chapters_chapter_section_loader_delete_middle_section(self): + test_slug = "middle-section" + chapter = self.test_data.create_chapter("1") + factory = mock.Mock() + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + self.assertQuerysetEqual( + ChapterSection.objects.all(), + [ + "", + "", + "" + ] + ) + + # Delete the middle section from the database + test_slug = "multiple-sections" + chapter_section_loader = ChapterSectionsLoader( + factory, + chapter, + base_path=self.base_path, + content_path=test_slug, + structure_filename="{}.yaml".format(test_slug), + ) + chapter_section_loader.load() + self.assertQuerysetEqual( + ChapterSection.objects.all(), + [ + "", + "" + ] + ) diff --git a/csfieldguide/tests/chapters/management/assets/non-sequential-chapter-numbers/structure/structure.yaml b/csfieldguide/tests/chapters/management/assets/non-sequential-chapter-numbers/structure/structure.yaml new file mode 100644 index 000000000..1636ce72c --- /dev/null +++ b/csfieldguide/tests/chapters/management/assets/non-sequential-chapter-numbers/structure/structure.yaml @@ -0,0 +1,5 @@ +chapters: + introduction: + chapter-number: 1 + algorithms: + chapter-number: 3 diff --git a/csfieldguide/tests/chapters/management/test_loadtopics_command.py b/csfieldguide/tests/chapters/management/test_loadchapters_command.py similarity index 89% rename from csfieldguide/tests/chapters/management/test_loadtopics_command.py rename to csfieldguide/tests/chapters/management/test_loadchapters_command.py index 04fabd3fe..4e0cb3fb5 100644 --- a/csfieldguide/tests/chapters/management/test_loadtopics_command.py +++ b/csfieldguide/tests/chapters/management/test_loadchapters_command.py @@ -153,3 +153,21 @@ def test_loadchapters_chapters_number_invalid(self, chapter_loader, glossary_loa management.call_command, "loadchapters" ) + + @mock.patch( + "chapters.management.commands._GlossaryTermsLoader.GlossaryTermsLoader.load", + return_value=True + ) + @mock.patch( + "chapters.management.commands._ChaptersLoader.ChaptersLoader.load", + return_value=True + ) + @override_settings( + CHAPTERS_CONTENT_BASE_PATH=os.path.join(CHAPTERS_PATH, "non-sequential-chapter-numbers") + ) + def test_loadchapters_chapters_non_sequential(self, chapter_loader, glossary_loader): + self.assertRaises( + InvalidYAMLValueError, + management.call_command, + "loadchapters" + ) diff --git a/dev b/dev index 29ac49e28..d5afe5282 100755 --- a/dev +++ b/dev @@ -15,6 +15,8 @@ set -e +export DOCKER_UID=$UID + ERROR='\033[0;31m' SUCCESS='\033[0;32m' CODE='\033[0;36m' diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 3e7e7349b..9c04dac86 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -33,6 +33,8 @@ services: build: context: . dockerfile: ./infrastructure/local/node/Dockerfile + args: + DOCKER_UID: ${DOCKER_UID} image: cs_field_guide_local_node volumes: # https://burnedikt.com/dockerized-node-development-and-mounting-node-volumes/#exclude-node_modules-from-the-mount diff --git a/docs/_static/html_snippets/chapters_directory_structure.html b/docs/_static/html_snippets/chapters_directory_structure.html new file mode 100644 index 000000000..b3a6404e7 --- /dev/null +++ b/docs/_static/html_snippets/chapters_directory_structure.html @@ -0,0 +1,16 @@ +
    + ├── chapters/
    + │   ├── content/
    + │   │   ├── en/
    + │   │   │   └── new-chapter/
    + │   │   │      ├── sections/
    + │   │   │      │   ├── first-section.md
    + │   │   │      │   └── another-section.md
    + │   │   │      └── new-chapter.md
    + │   │   └── structure/
    + │   │      ├── new-chapter/
    + │   │      │   ├── sections/
    + │   │      │   │   └── sections.yaml
    + │   │      │   └── new-chapter.yaml
    + │   │      └── structure.yaml
    +
    diff --git a/docs/_static/html_snippets/project_directory_tree.html b/docs/_static/html_snippets/project_directory_tree.html index 73b10c461..0f4e70cca 100644 --- a/docs/_static/html_snippets/project_directory_tree.html +++ b/docs/_static/html_snippets/project_directory_tree.html @@ -1,14 +1,19 @@
    ├── csfieldguide/
    + │   ├── appendices/
    │   ├── build/
    + │   ├── chapters/
    │   ├── config/
    + │   ├── curriculum_guides/
    │   ├── general/
    + │   ├── interactives/
    │   ├── locale/
    - │   ├── resources/
    + │   ├── search/
    │   ├── static/
    - │   ├── temp/
    + │   ├── staticfiles/
    │   ├── templates/
    - │   ├── topics/
    + │   ├── tests/
    + │   ├── third-party-licences/
    │   ├── utils/
    │   ├── gulpfile.js
    │   ├── make-interactive-thumbnails.js
    diff --git a/docs/author/chapters.rst b/docs/author/chapters.rst index 5235f9807..0ee37e913 100644 --- a/docs/author/chapters.rst +++ b/docs/author/chapters.rst @@ -103,6 +103,7 @@ A complete chapter application structure file with multiple chapters may look li chapter-number: 2 glossary-folder: glossary +.. _chapter-configuration-file: Chapter Configuration File ------------------------------------------------------------------------------ @@ -133,6 +134,8 @@ A complete chapter structure file may look like the following: icon: svg/introduction-icon.svg sections: sections/sections.yaml +.. _chapter-sections-configuration-file: + Chapter Sections Configuration File ------------------------------------------------------------------------------ diff --git a/docs/author/chapters_quick_start.rst b/docs/author/chapters_quick_start.rst new file mode 100644 index 000000000..a7e24a715 --- /dev/null +++ b/docs/author/chapters_quick_start.rst @@ -0,0 +1,196 @@ +Quick Start - Adding a Chapter +############################## + +Prerequisites +============= + +1. The development server is running (see :ref:`installation-check-project-setup-works`). +2. `(optional)` A basic understanding of Markdown. + +A small note on structure +========================= + +CSFG content is described using a combination of YAML (see `here`__ for a quick run down), +for the structure, and an extension of Markdown that we have designed called Verto +(see `here`__), used for the actual content that is displayed to the user. + +Most content in CSFG, including Chapters and Curriculum Guides, use a very similar folder structure. +This means that once you've learnt how to create a Chapter, it should be relatively simple to look at +adding other content that uses the same structure. + +Creating a new chapter +====================== + +Chapter structure +----------------- + +Navigate to ``./csfieldguide/chapters/content``. +This is where the content for all of the chapters are stored. +We can see a few subdirectories. +Most of thes are for languages, these contain localised Verto files of the different chapters, broken up by section. +The last folder is the ``structure`` folder. +This is used to describe how the chapters and sections are laid out and ordered. + +Here is a diagram of the directory structure at the end of this quick start: + +.. raw:: html + :file: ../_static/html_snippets/chapters_directory_structure.html + +We will start by creating a chapter called "New Chapter". + +Configuration +------------- + +1. Under the ``structure`` subdirectory, create a new folder with the name ``new-chapter``. This name is referred to as the chapter's `slug`_. + It must be lowercase with hyphens separating words. +2. Inside this folder we can create a chapter configuration file called ``new-chapter.yaml``. + This contains some basic information about the chapter. For now, just paste this information into it: + + .. code-block:: yaml + + icon: svg/csfg-icon.svg + sections: sections/sections.yaml + + + See the :ref:`chapter-configuration-file` documentation for more info. + +3. Staying inside the ``new-chapter folder``, create another folder called ``sections`` and add a file inside that is called ``sections.yaml`` with this content: + + .. code-block:: yaml + + first-section: + section-number: 1 + + another-section: + section-number: 2 + + This is called the :ref:`chapter-sections-configuration-file`, and it describes what order the different sections based off of their slugs. + +4. Finally, we need to add the chapter to the list of chapters in ``content/structure/structure.yaml``. This is the :ref:`application-structure-file`. + Just below the last chapter definition, add this code with the value in chapter-number replaced with the last chapter-number in the file plus 1. As of writing this, the last chapter number is 18, so I put 19 (Make sure the indentation is the same as the previous lines, YAML is very specific about indentation): + + .. code-block:: yaml + + new-chapter: + chapter-number: 19 + +Writing the Markdown content +---------------------------- + +Okay, now that we're done with the boring configuration, lets get on to writing some content! +You can find a reference of all available Markdown and Verto formatting :doc:`in our writing guide `, but a lot of the useful tags also have examples below. + +1. First we need a directory for the content to go in. Under ``content/en`` create a folder called ``new-chapter``. +2. Let's start with the introduction page. Under this new folder, create a new file called ``new-chapter.md`` and add this text to it: + + .. code-block:: markdown + + # New Chapter (this text becomes the title of the chapter) + + This is the opening page of the chapter. + The Heading 1 (#) becomes the title at the top of this page, and there should be an + "Introduction" header just underneath it, when it is rendered. + Multiple sets of whitespace (spaces, tabs, or new lines) are typically shrunken + down to a single space, making it easy to nicely format the code without changing + the formatting of the rendered page. + + To put a new line in the rendered page, put two new lines in the page. + So, this should all be a seperate paragraph from the above text. + You can do **bold**, *italic*, [links](https://csfieldguide.org.nz) and much more. + + ### Heading 3 + You can have up to 6 headings by using different numbers of #'s. + {comment This is a comment, and it won't be visible in the rendered page} + +3. For the sections, create a new subdirectory of ``new-chapter`` and call it ``sections``. For every slug that you defined in step 3 of Configuration, you want to create a file called `.md` (replace with the name of the slug). Here’s some example verto for the two sections, but feel free to come up with your own as well 🙂: + + ``first-section.md``: + + .. code-block:: markdown + + # This becomes the title of this section + + Just like for the chapter, the Heading 1 at the top of the file becomes the title + on the rendered page. + + ## Some cool things you can do + + {blockquote} + + Blockquotes are cool! + Also for any jargon such as the word {glossary-link term="lossless"}lossless{glossary-link end}, + you can link to the glossary (assuming there is a defintion for the term. + + {blockquote end} + + {image file-path="img/chapters/jflap-create-state.png" alt="Building an FSA – example" caption="true"} + + This is the caption text. + + {image end} + + ### Video + + {video url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"} + + ``another-section.md``: + + .. code-block:: markdown + + # The other section + + ## Panels!!!! + + {panel type="curiosity"} + + # Panels need a title + + How curious + + {panel end}https://cs-field-guide.readthedocs.io/en/latest/author/writing_guide.html + + {panel type="challenge"} + + # There are many different types of panels + + {blockquote} + + You can have other verto features inside panels too!! + + {blockquote end} + + {panel end} + + Below is a teacher panel, if you don't see it, change to teacher mode: + + {panel type="teacher"} + + # Hidden for everyone but teachers :) + + This is hidden unless you are in teacher mode + + {panel end} + + ## And lastly... interactives + This is an in-page interactive: + + {interactive slug="binary-cards" type="in-page"} + + And this is a link to a whole page interactive: + + {interactive slug="binary-cards" type="whole-page" parameters="digits=5&start=BBBBB" text="true"} + + Binary Cards Interactive + + {interactive end} + +Updating the database +===================== + +Lastly, we need to update the database. +Run ``./dev update_data``, and you should be able to see your new chapter when you refresh the page! + +__ https://cs-field-guide.readthedocs.io/en/latest/author/understanding_configuration_files.html +__ https://cs-field-guide.readthedocs.io/en/latest/author/writing_guide.html + +.. _slug: https://en.wikipedia.org/wiki/Clean_URL#Slug diff --git a/docs/author/index.rst b/docs/author/index.rst index 2b9bb638a..4dae3c947 100644 --- a/docs/author/index.rst +++ b/docs/author/index.rst @@ -7,6 +7,7 @@ The following pages are for those wanting to write or edit Computer Science Fiel :maxdepth: 1 :caption: Contents + chapters_quick_start writing_guide understanding_configuration_files chapters diff --git a/docs/changelog.rst b/docs/changelog.rst index 9ca2aa737..485feae28 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -18,6 +18,39 @@ All notable changes to this project will be documented in this file. We have listed major changes for each release below. `All downloads are available on GitHub `__ +3.13.0 +============================================================================== + +**Release date:** 15th December 2022 + +**Changelog:** + +- Add section about Shannon's Experiment to the Compression chapter. +- Add more comprehensive Getting Started documentation. +- Add an "Adding a chapter" section to the Author Documentation. +- Update infrastructure to fix issues with local development on multi-user Linux machines. +- Fix majority of loading bugs caused by the 3.12.6 release. +- Core dependency changes: + + - Update crowdin/github-action from 1.5.0 to 1.5.2 + - Update decode-uri-component from 0.2.0 to 0.2.2 + - Update dependabot/fetch-metadata from v1.3.4 to v1.3.5 + - Update django-debug-toolbar from 3.7.0 to 3.8.1 + - Update django-modeltranslation from 0.18.5 to 0.18.7 + - Update engine.io from 6.2.0 to 6.2.1 + - Update jquery from 3.6.1 to 3.6.2 + - Update lxml from 4.9.1 to 4.9.2 + - Update postcss from 8.4.18 to 8.4.20 + - Update sass from 1.55.0 to 1.56.2 + - Update selenium from 4.5.0 to 4.7.0 + - Update socket.io-parser from 4.0.4 to 4.0.5 + - Update sphinx-rtd-theme from 1.1.0 to 1.1.1 + - Update yargs from 17.6.0 to 17.6.2 + +- Interactive scene-editor dependency changes: + + - Update three from 0.146.0 to 0.147.0 + 3.12.6 ============================================================================== @@ -25,7 +58,7 @@ We have listed major changes for each release below. **Changelog:** -- Update content rather than recreating it when `update_data` script is run. +- Update content rather than recreating it when ``update_data`` script is run. - Dependency updates: - Update crowdin/github-action from 1.4.16 to 1.5.0. diff --git a/docs/getting_started/code_of_conduct.rst b/docs/getting_started/code_of_conduct.rst new file mode 100644 index 000000000..ba4c30c97 --- /dev/null +++ b/docs/getting_started/code_of_conduct.rst @@ -0,0 +1,83 @@ +Code of Conduct +############################################################################## + +Our Pledge +============================================================================== + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +Our Standards +============================================================================== + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention + or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +Our Responsibilities +============================================================================== + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +Scope +============================================================================== + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +Enforcement +============================================================================== + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at +``csse-education-research@canterbury.ac.nz``. +All complaints will be reviewed and investigated and will result in a response +that is deemed necessary and appropriate to the circumstances. The project +team is obligated to maintain confidentiality with regard to the reporter of +an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +Attribution +============================================================================== + +This Code of Conduct is adapted from the `Contributor Covenant`_, +`version 1.4`_. + +.. _version 1.4: http://contributor-covenant.org/version/1/4/ +.. _Contributor Covenant: http://contributor-covenant.org/ diff --git a/docs/getting_started/contributing_guide.rst b/docs/getting_started/contributing_guide.rst new file mode 100644 index 000000000..e6d716b41 --- /dev/null +++ b/docs/getting_started/contributing_guide.rst @@ -0,0 +1,234 @@ +Contributing Guide +############################################################################## + +This page lists a set of guidelines for contributing to the project. +These are just guidelines, not rules, use your best judgment and feel +free to propose changes to this document in a pull request. + +Reporting Issues and Making Suggestions +============================================================================== + +This section guides you through submitting an issue or making a suggestion +for the CS Unplugged project. +Following these guidelines helps maintainers and the community understand +your findings. + +Before Submitting an Issue +------------------------------------------------------------------------------ + +- `Search the issue tracker for the issue/suggestion`_ to see if it has + already been logged. + If it has, add a comment to the existing issue (even if the issue is closed) + instead of opening a new one. + +How do I Submit a Good Issue or Suggestion? +------------------------------------------------------------------------------ + +Issues are tracked in the GitHub issue tracker (if you've never used +GitHub issues before, read this `10 minute guide to become a master`_). +When creating an issue, explain the problem and include additional details to +help maintainers understand or reproduce the problem: + +For Reporting an Issue +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- **Use a clear and descriptive title** for the issue to identify the problem. +- **Clearly and concisely describe the issue** and provide screenshots if + required. +- **Link any related existing issues**. + +If the issues is a code related issue, also include the following: + +- **Describe the exact steps which reproduce the problem** in as many details + as possible. + For example, how you were generating a resource. + When listing steps, **don't just say what you did, explain how you did it**. +- **Explain which behavior you expected to see instead and why.** +- **Describe the behavior you observed after following the steps** and point + out what exactly is the problem with that behavior. +- **Can you reliably reproduce the issue?** If not, provide details about + how often the problem happens and under which conditions it normally happens. +- **Include screenshots or animated GIFs** if it helps explain the issue you + encountered. +- **What's the name and version of the OS you're using?** +- **What's the name and version of the browser you're using?** +- **If the problem is related to performance**, please provide + specifications of your computer. + +For Making a Suggestion +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Explain the suggestion and include additional details to help maintainers +understand the changes: + +- **Use a clear and descriptive title** for the issue to identify the + suggestion. +- **Clearly and concisely describe the suggestion** and provide screenshots if + required. +- **Explain why this suggestion would be useful** to most CS Unplugged users + and isn't something that should be a implemented as a community variant of + the project. +- **Link any related existing suggestions**. + +.. note:: + + **Internal Staff Only:** Assigning Issues + + Our policy is to only assign a person to an issue when they are actively + working on the issue. + Please don't assign yourself when you *plan* to do the task (for example: + in the next few days), assign yourself when you begin work. + This allows other team members to clearly see which tasks are available + to be worked on. + +Your First Code Contribution (pull request) +============================================================================== + +Unsure where to begin contributing to CS Unplugged? +You can start by looking through the `issue tracker`_. + +Pull Requests +------------------------------------------------------------------------------ + +- **Include a detailed explaination** of the proposed change, including + screenshots and animated GIFs in your pull request whenever possible. +- **Read and apply the style guides** listed below. +- Your pull request should be on a new branch from our ``develop`` branch, + **that is being requested to merge back into** ``develop``. + The naming conventions of branches should be descriptive of the new + addition/modification. + Ideally they would specify their namespace as well, for example: + + - ``resource/puzzle-town`` + - ``topic/algorithms`` + - ``issue/234`` + +- Link to any relevant existing issues/suggestions. +- Add necessary documentation (if appropriate). + +We aim to keep the CS Unplugged project as robust as possible, so please do +your best to ensure your changes won't break anything! + +Style and Etiquette Guides +============================================================================== + +Git +------------------------------------------------------------------------------ + +- Commits should be as descriptive as possible. + Other developers (and even future you) will thank you for your forethought + and verbosity for well documented commits. + Generally: + + - Limit the first line to 72 characters or less + - Reference issues and pull requests liberally + +- We use `Vincent Driessen's Git Branching Model `_ + for managing development. + Please read this document to understand our branching methods, and how + to perform clear branches and merges. + + Specifically for our respository: + + - We create a new branch for each task of work, no matter how small it is. + - We create the branch off the ``develop`` branch. + - In general, the new branch should begin with ``issue/`` followed by + the issue number. + - When a branch is completed, a pull request is created on GitHub for + review. + - Branches are merged back into ``develop``. + +GitHub +------------------------------------------------------------------------------ + +.. note:: + + Internal Staff Only + +- Mention a user (using the ``@`` symbol) when an issue is relevant to them. +- Only assign yourself to an issue, when you are actively working on it. +- The technical team may tag an author to review specific pull requests, and as + a reviewer you can either approve, request changes, or just leave comments. +- A pull request requires one review approval to be merged. +- If multiple people are tagged as reviewers, we only need one review (unless + otherwise specified). + For example: For content changes, we ask that at least one member from each + of the content and technical teams reviews the pull request. +- The creator of the pull request should assign all those suitable for review. +- The creator of the pull request is the only person who should merge the pull + request. + If you approve a pull request and it shows the big green button, please + resist clicking it! + +Project Structure +------------------------------------------------------------------------------ + +- Directories should be all lowercase with dashes for spaces. +- Directories and files should use full words when named, however JavaScript, + CSS, and image directories can be named ``js/``, ``css/``, and ``img/`` + respectively. + +Text (Markdown) +------------------------------------------------------------------------------ + +- Each sentence should be started on a newline (this greatly improves + readability when comparing two states of a document). + +Programming +------------------------------------------------------------------------------ + +Quote from Google style guides: + + Be consistent. + + If you’re editing code, take a few minutes to look at the code around you + and determine its style. + If they use spaces around all their arithmetic operators, you should too. + If their comments have little boxes of hash marks around them, make your + comments have little boxes of hash marks around them too. + + The point of having style guidelines is to have a common vocabulary of coding + so people can concentrate on what you’re saying rather than on how you’re + saying it. + We present global style rules here so people know the vocabulary, but local + style is also important. + If code you add to a file looks drastically different from the existing code + around it, it throws readers out of their rhythm when they go to read it. + Avoid this. + +We aim to abide by the following style guides: + +- **Python** - We follow `PEP8`_ except for one change of line length. + `Django recommends allowing 119 characters`_, so we use this as our line + length limit. + This style is enforced by the `flake8`_ style checker. +- **HTML** - We follow the `open source HTML style guide`_ by @mdo. +- **CSS** - We follow the `open source CSS style guide`_ by @mdo. +- **JavaScript** - We follow the `Google JavaScript style guide`_. + +Licencing +------------------------------------------------------------------------------ + +Any third-party libraries or packages used within this project should have +their listed within the ``LICENCE-THIRD-PARTY`` file, with a full copy of the +licence available within the ``third-party-licences`` directory. + +Final Comments +============================================================================== + +After reading the sections above, you should be able to answer the following +questions: + +- When do I create a issue and how do I describe it? +- When and how do I create a new Git branch to work on? +- *Internal staff only:* When do I assign myself to an issue? + +.. _Search the issue tracker for the issue/suggestion: https://github.com/uccser/cs-unplugged/issues?utf8=%E2%9C%93&q=is%3Aissue +.. _10 minute guide to become a master: https://guides.github.com/features/issues/ +.. _issue tracker: https://github.com/uccser/cs-unplugged/issues +.. _PEP8: https://www.python.org/dev/peps/pep-0008/ +.. _Django recommends allowing 119 characters: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/ +.. _open source HTML style guide: http://codeguide.co/#html +.. _open source CSS style guide: http://codeguide.co/#css +.. _Google JavaScript style guide: https://google.github.io/styleguide/javascriptguide.xml +.. _flake8: http://flake8.pycqa.org/en/latest/ diff --git a/docs/getting_started/helper_commands.rst b/docs/getting_started/helper_commands.rst new file mode 100644 index 000000000..0b2c742b8 --- /dev/null +++ b/docs/getting_started/helper_commands.rst @@ -0,0 +1,323 @@ +Helper Commands for Developing +############################################################################## + +.. note:: + + We assume by this point you have installed the project, checked the setup is working (see :ref:`installation-check-project-setup-works`), and also have a basic understanding of the :doc:`project structure `. + +The CS Field GUide project uses many systems (Django, Docker, Gulp, etc) to run, so we have written a script for running groups of commands for running the system while developing. + +The script is called ``dev`` and can be found in the ``cs-field-guide`` folder of the repository. +To run this script, open a terminal window in the directory and enter the following command (you don't need to enter the ``$`` character, this shows the start of your terminal prompt): + +.. code-block:: bash + + $ ./dev [COMMAND] + +Where ``[COMMAND]`` is a word from the list below: + +- :ref:`build` +- :ref:`collect_static` +- :ref:`compilemessages` +- :ref:`end` +- :ref:`exec` +- :ref:`help` +- :ref:`logs` +- :ref:`make_interactive_thumbnails` +- :ref:`makemessages` +- :ref:`makemigrations` +- :ref:`migrate` +- :ref:`reboot_django` +- :ref:`rebuild_index` +- :ref:`restart` +- :ref:`start` +- :ref:`static` +- :ref:`style` +- :ref:`test_backwards` +- :ref:`test_coverage` +- :ref:`test_specific` +- :ref:`test_suite` +- :ref:`update` +- :ref:`update_data` +- :ref:`update_static` + +All users of the project (content and technical developers) should become familiar with the following commands: + +- :ref:`start` +- :ref:`end` +- :ref:`build` +- :ref:`update` + +----------------------------------------------------------------------------- + +.. _build: + +``build`` +============================================================================== + +Running ``./dev build`` will build or rebuild the Docker images that are required for the CS Unplugged system. + +----------------------------------------------------------------------------- + +.. _collect_static: + +``collect_static`` +============================================================================== + +Running ``./dev collect_static`` runs the Django ``collectstatic`` command to collect static files. +It will copy files under the ``static/`` folder into the ``staticfiles/`` folder. + +----------------------------------------------------------------------------- + +.. _compilemessages: + +``compilemessages`` +============================================================================== + +Running ``./dev compilemessages`` runs the Django ``compilemessages`` command. +This runs over ``.po`` files and creates ``.mo`` files which are optimised for use by ``gettext``. +You will need to run this command after each time you create your message file or each time you make changes to it. + +----------------------------------------------------------------------------- + +.. _end: + +``end`` +============================================================================== + +Running ``./dev end`` will stop any containers which are currently running, this usually takes 10 to 20 seconds. + +----------------------------------------------------------------------------- + +.. _exec: + +``exec`` +============================================================================== + +Running ``./dev exec`` allows you to run a command inside a container. +This is commonly used to run a bash shell, allowing you to inspect +what is going on inside the container. + +As an example, ``./dev exec django bash`` would be the equivalent to entering +``docker compose run django bash`` + +----------------------------------------------------------------------------- + +.. _help: + +``help`` +============================================================================== + +Running ``./dev help`` displays brief help text for the script. +More details for each command can be found on this page. + +----------------------------------------------------------------------------- + +.. _logs: + +``logs`` +============================================================================== + +Running ``./dev logs`` will display the logs for the running systems. +The output is for all logs until the time the command was run, therefore successive calls may display new logs. + +To follow logs as they output, enter ``docker compose logs --follow``. + + +----------------------------------------------------------------------------- + +.. _make_interactive_thumbnails: + +``make_interactive_thumbnails`` +============================================================================== + +Running ``./dev make_interactive_thumbnails`` generates the thumbnails for +the interactives. + +----------------------------------------------------------------------------- + +.. _makemessages: + +``makemessages`` +============================================================================== + +Running ``./dev makemessages`` runs the Djanog ``makemessages`` command. +This will create message files where each message file represents a single language. +Message files contain all available translation strings and how they should be represented in the given language. + +----------------------------------------------------------------------------- + +.. _makemigrations: + +``makemigrations`` +============================================================================== + +Running ``./dev makemigrations`` runs the Django ``makemigrations`` command to create migration files. + +----------------------------------------------------------------------------- + +.. _migrate: + +``migrate`` +============================================================================== + +Running ``./dev migrate`` runs the Django ``migrate`` command to apply migration files. + +----------------------------------------------------------------------------- + +.. _reboot_django: + +``reboot_django`` +============================================================================== + +Running ``./dev reboot_django`` will rebuild the Django Docker container. + +----------------------------------------------------------------------------- + +.. _rebuild_index: + +``rebuild_index`` +============================================================================== + +Running ``./dev rebuild_index`` will rebuild the search indexes. + +----------------------------------------------------------------------------- + +.. _restart: + +``restart`` +============================================================================== + +Running ``./dev restart`` is a shortcut for running: + +- ``./dev end`` +- ``./dev start`` + +More details for each command can be found on this page. + +----------------------------------------------------------------------------- + +.. _start: + +``start`` +============================================================================== + +Running ``./dev start`` starts the development environment. +It performs the following tasks: + +- Build system Docker images if required (see below) +- Start the Django website system +- Start the Nginx server to display the website and static files +- Start the database server + +When you run this command for the first time on a computer it will also run ``./dev build`` to build the system Docker images. +This can take some time, roughly 15 to 30 minutes, depending on your computer and internet speed. +Images are only required to be built once, unless the image specifications change (you can rebuild the images with ``./dev build``). +Once the images are built, the script will run these images in containers. + +Once the development environment is operational, run the ``./dev update`` command to load the CS Unplugged content. + +----------------------------------------------------------------------------- + +.. _static: + +``static`` +============================================================================== + +Running ``./dev static`` runs the commands for generating the static files for the website. + +If changes are made to the static files (for example, a new image is added) when the system is running, this command needs to be entered to view the new files on the website. + +.. _style: + +``style`` +============================================================================== + +Running ``./dev style`` will run the ``flake8`` and ``pydocstyle`` commands to check the style of the project. +If the output is ``0`` for a check, then there are zero errors. + +----------------------------------------------------------------------------- + +.. _test_backwards: + +``test_backwards`` +============================================================================== + +Running ``./dev test_backwards`` will run the test suite in reverse. +This is useful to check if any tests are influencing the result of each other. +If this command if run on Travis CI, it will only run for a pull request. + +----------------------------------------------------------------------------- + +.. _test_coverage: + +``test_coverage`` +============================================================================== + +Running ``./dev test_coverage`` will display a table detailing test code coverage, from the report generated by ``./dev test``. + +----------------------------------------------------------------------------- + +.. _test_specific: + +``test_specific`` +============================================================================== + +Running ``./dev test_specific [MODULE_PATH]`` will run a specific test module. +For example, running ``./dev test_specific tests.resources.views.test_index_view`` will only run the tests for checking the index view of the resources application. + +----------------------------------------------------------------------------- + +.. _test_suite: + +``test_suite`` +============================================================================== + +Running ``./dev test_suite`` will run the test suite, and create a report detailing test code coverage. +The code coverage report can be displayed by running ``./dev test_coverage``. + +----------------------------------------------------------------------------- + +.. _update: + +``update`` +============================================================================== + +Running ``./dev update`` performs the following tasks: + +- Update the database with the required structure (known as the schema) +- Load the CS Unplugged content into the database +- Create the required static files + +Once the script has performed all these tasks, the script will let you know the website is ready. +Open your preferred web browser to the URL ``cs-unplugged.localhost`` to view the website. + +In more detail, ``./dev update`` runs the Django ``makemigratations`` and ``migrate`` commands for updating the database schema, and then runs the custom ``updatedata`` command to load the topics content into the database. +It also runs the ``static`` command to generate static files. + +If changes are made to the topics content when the system is running, this command needs to be run to view the new changes on the website. + +----------------------------------------------------------------------------- + +.. _update_data: + +``update_data`` +============================================================================== + +Running ``./dev update_data`` runs the custom ``update_data`` command to load the topics content into the database. + +----------------------------------------------------------------------------- + +.. _update_static: + +``update_static`` +============================================================================== + +Running ``./dev update_static`` generates and collects static files into the ``staticfiles/`` folder. +This command is equivalent to running ``static`` and then ``collect_static``. + +----------------------------------------------------------------------------- + +You now know the basic commands for using the CS Field Guide system. +You are now ready to tackle the documentation for the area you wish to contribute on. +Head back to the :doc:`documentation homepage <../index>` and choose the documentation related to the task you wish to contribute to. diff --git a/docs/getting_started/index.rst b/docs/getting_started/index.rst new file mode 100644 index 000000000..7d3683896 --- /dev/null +++ b/docs/getting_started/index.rst @@ -0,0 +1,28 @@ +Getting Started +############################################################################## + +This documentation will help you understand how the project is setup, +basic steps on how to use it, and our guidelines for your contributions. + +.. note:: + + This project adheres to the Contributor Covenant code of conduct. + By participating, you are expected to uphold this code. + Please read our :doc:`Code of Conduct ` before continuing. + You can report unacceptable behaviour by + `emailing us `_. + +.. toctree:: + :hidden: + + code_of_conduct + +.. toctree:: + :maxdepth: 1 + :caption: Contents + + contributing_guide + installation + project_structure + helper_commands + UCCSER Development Guide diff --git a/docs/getting_started/installation.rst b/docs/getting_started/installation.rst new file mode 100644 index 000000000..1d0c0afdd --- /dev/null +++ b/docs/getting_started/installation.rst @@ -0,0 +1,189 @@ +Installation Guide +################################################# + +This page will set your machine up for working on the CS Field Guide project. +You should only need to do these installation steps once (unless the required +steps for setup change). + +Requirements +================================================= + +- At least 5 GB of hard drive space. +- An internet connection to download 1 to 2 GB of data. + +Recommended Reading +================================================= + +If you aren't familiar with the following systems, we recommend +reading tutorials first on how to use them: + +- Entering terminal commands for your operating system +- Git (here are two Git tutorials: `one`_ `two`_) + +Step 1: Install Git +================================================= + +Install the version control software `Git`_ onto your computer. + +.. note:: + + If you are new to Git and not comfortable with using the terminal, + you may like to use a free program like `SourceTree`_ to use Git. + +Step 3: Create GitHub Account +================================================= + +If you don't already have an account on GitHub, create a free account on +the `GitHub website`_. +This account will be tied to any changes you submit to the project. + +Step 4: Set Git Account Values +================================================= + +When you make a commit in Git (the term for changes to the project), the +commit is tied to a name and email address. We need to set name and email +address within the Git system installed on the machine. + +- `Setting your username in Git`_ +- `Setting your email in Git`_ + +You can also `keep your email address private on GitHub`_ if needed. + +.. note:: + + If your GitHub account is secured with two-factor authentication (2FA) + this is a perfect time to setup `SSH keys`_. + +Step 5: Download the CS Field Guide Repository +================================================= + +Firstly create the directory you wish to hold the CS Field Guide repository +directory in if you wish to store the data in a specific location. +Once you have decided upon the location, clone (the Git term for download) the +project onto your computer. + +If you are using terminal commands to use Git, type the following command in +terminal (you don't need to enter the ``$`` character, this shows the start of +your terminal prompt): + +.. code-block:: bash + + $ git clone https://github.com/uccser/cs-field-guide.git + +.. note:: + + If you connect to GitHub through SSH, then type: + + .. code-block:: bash + + $ git clone git@github.com:uccser/cs-field-guide.git + + This may be necessary if you use two-factor authentication to login to GitHub. + See the note in Step 4 for help with setting up SSH keys with this. + +Once Git has cloned the directory, checkout the repository to the development +branch ``develop``. + +Step 6: Install Docker +================================================= + +We use a system called `Docker`_ to run the CS Field Guide system, both on local +machine for development, and also when deployed to production. + +Linux +----- + +While it is possible to `install Docker Desktop`_ on Linux, +we recommend installing `Docker Engine`_ and `Docker Compose`_ individually. + +With these installed, for our dev script to work, you need to be able to manage docker +as a non root user. See `these instructions`_ on how to do this. + +Once you have installed the software, run the following commands in a terminal +to check Docker is working as intended (you don't need to enter the ``$`` +character, this shows the start of your terminal prompt). + +.. code-block:: bash + + $ docker version + $ docker compose version + $ docker run hello-world + +Windows +------- + +If you are using Windows, we highly recommend using Docker Desktop in +combination with the Windows Subsystem for Linux. See `here`_ for +installation instructions. + +Mac +--- + +If you are using macOS, Docker Desktop would likely be your best bet, however +we haven't tested the installation on macOS yet. + +Step 7: Install Text Editor/IDE (optional) +================================================= + +This is a good time to install your preferred IDE or text editor, if you don't +have one already. +Some free options we love: + +- `Visual Studio Code`_ +- `Sublime Text`_ + +.. _installation-check-project-setup-works: + +Step 8: Check Project Setup Works +================================================= + +To check the project works, open a terminal in the project root directory, +which is the ``cs-field_guide/`` directory (should contain a file called +``dev``). + +Type the following commands into the terminal (we will cover these commands +in more detail on the next page): + +.. code-block:: bash + + $ ./dev start + $ ./dev update + +If this is the first time you're running this script, it will need to build +system images. +This can take some time, roughly 15 to 30 minutes, depending on your computer +and internet speed (we recommend grabbing a cup of tea and watching an episode +of Brooklyn Nine-Nine on Netflix). + +After the helper script builds the system images, it will automatically start +the system, and will let you know when the system is ready. +You should then be able to open your preferred web browser to the URL +``cs-field-guide.localhost`` and see the CS Field Guide homepage. + +If you are working on documentation, navigate to ``docs.cs-field-guide.localhost``. +This is a live server, so any changes you make to the documentation should be +visible on the webpage within a couple seconds of saving the file. + +Congratulations if you made it this far and everything is working, +you're all set to contribute to the CS Field Guide project. + +.. _one: https://git-scm.com/docs/gittutorial +.. _two: https://try.github.io/levels/1/challenges/1 +.. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io/en/latest/ +.. _Git: https://git-scm.com/ +.. _SourceTree: https://www.sourcetreeapp.com/ +.. _GitHub website: https://github.com/ +.. _SSH keys: https://help.github.com/articles/connecting-to-github-with-ssh/ +.. _Setting your username in Git: https://help.github.com/articles/setting-your-username-in-git/ +.. _Setting your email in Git: https://help.github.com/articles/setting-your-email-in-git/ +.. _keep your email address private on GitHub: https://help.github.com/articles/keeping-your-email-address-private/ +.. _Docker: https://www.docker.com/ +.. _Docker Store: https://store.docker.com/search?type=edition&offering=community +.. _Verto documentation: http://verto.readthedocs.io/en/latest/install.html +.. _Visual Studio Code: https://code.visualstudio.com/ +.. _Sublime Text: https://www.sublimetext.com/ +.. _here: https://docs.docker.com/desktop/windows/wsl/ +.. _install Docker Desktop: https://docs.docker.com/desktop/install/linux-install/ +.. _Docker Engine: https://docs.docker.com/engine/install/#server +.. _Docker Compose: https://docs.docker.com/compose/install/#scenario-two-install-the-compose-plugin +.. _these instructions: https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user diff --git a/docs/getting_started/project_structure.rst b/docs/getting_started/project_structure.rst new file mode 100644 index 000000000..9567b401b --- /dev/null +++ b/docs/getting_started/project_structure.rst @@ -0,0 +1,137 @@ +Project Structure +########################################### + +This page covers the structure of the CS Field Guide project. +The following diagram will be helpful when reading the following sections: + +.. raw:: html + :file: ../_static/html_snippets/project_directory_tree.html + +Repository Directory +================================================= + +The repository directory (or root directory) contains the following: + +- ``csfieldguide/`` + + - This directory contains the Django web system for the CS Field Guide website. + This includes all raw text content, images, resources, etc. + +- ``docs/`` + + - This directory contains the documentation for the repository (which includes + the file you are reading now). + +- ``subtitles/`` + + - This directory contains subtitle files for CS Field Guide videos. + +- ``README.md`` + + - This file contains an introduction and important information for the + repository. + +- ``LICENCE.md`` + + - This file details the licences the repository uses. + +- Plus other files used for installation and repository configuration. + +csfieldguide Directory +================================================= + +The ``csfieldguide/`` directory holds the Django web system and is split across +the following directories: + +- ``config/`` + + - This directory holds the settings used by the Django system. + It's unlikely you'll edit the contents of this directory unless you are + changing the Django configuration (for example: adding a new application). + +- ``tests/`` + + - This directory holds all the tests for the project. + +- ``utils/`` + + - Utility functions used throughout the application. One example would be custom + converter templates for Verto. + +.. _django-applications: + +Django contains 'applications' which are Python packages that provide +some set of features. +Each large part/chunk of the CS Unplugged is a separate application. +The project currently contains the following applications: + +- ``general/`` + + - This application displays webpages for generic pages on the website. + For example: homepage, about page, contact page, etc. + +- ``chapters/`` + + - The core CS Field Guide content is split across chapters, with each topic + containing any number of sections. THis applications stores and displays the + chapters content. + +- ``curriculum_guides/`` + + - This application displays a collection of curriculum guides to help + teachers use CS Field Guide to help teach their own curriculum. + +- ``interactives/`` + + - This application contains all of the interactive content used throughout + CS Field Guide. + +- ``search/`` + + - This application contains all the logic and ui to manage searching the entire website. + +See the Adding a Chapter quick start for an example of dealing with an application. + +The following directories are also required by the Django system: + +- ``static/`` + + - This directory contains non-user-generated media assets (for example: + images, JavaScript, CSS/SCSS, etc). + +- ``staticfiles/`` + + - This is where static files are served to the different applications + inside the project. + +- ``templates/`` + + - This directory contains all the HTML templates for the Django system. + +- ``locale/`` + + - This directory contains translations required for the Django system. + Translations for ``topics/`` are stored within the ``topics/content/``. + +The following directories are used when the server is running (for example: +a script compiles the SCSS to CSS and saves it to the ``build/`` directory for +serving on a webpage). +You should never save anything in these directories, as the contents are often +overwritten and cleared. + +- ``build/`` + + - Contains the generated output of the front-end script (for example: + compiled and minified CSS and JavaScript, compressed images, etc). + +- ``temp/`` + + - Contains temporary files used in creating generated files for + ``build`` directory. + +The ``csfieldguide/`` directory also contains the following files: + +- ``manage.py`` + + - A file created by Django used to manage the Django web system. + Don't modify the contents of this file. diff --git a/docs/index.rst b/docs/index.rst index 62ed65239..6e97288ef 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,7 +12,7 @@ The documentation is split into the following sections: ------------------------------------------------------------------------------ -`Getting Started Documentation `_ +:doc:`Getting Started Documentation ` ============================================================================== This documentation contains: @@ -20,6 +20,7 @@ This documentation contains: - Our expectations of your contributions to the project - Steps to install the project on your system for local development - Explainations on how the project is setup +- Quick Starts on how to write and develop for the project ------------------------------------------------------------------------------ @@ -53,7 +54,7 @@ This is the documentation to read if you want to do any of the following (or sim :maxdepth: 4 :caption: Table of Contents - Getting Started Documentation + getting_started/index author/index developer/index troubleshooting diff --git a/docs/requirements.txt b/docs/requirements.txt index 2c673d1b4..2cc8302a2 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,2 @@ sphinx==5.3.0 -sphinx-rtd-theme==1.1.0 +sphinx-rtd-theme==1.1.1 diff --git a/infrastructure/local/node/Dockerfile b/infrastructure/local/node/Dockerfile index 916deb477..896398c76 100644 --- a/infrastructure/local/node/Dockerfile +++ b/infrastructure/local/node/Dockerfile @@ -1,5 +1,8 @@ FROM node:14.20.0-buster +ARG DOCKER_UID=1000 +RUN usermod -u $DOCKER_UID node + # Install required system dependencies RUN apt-get update && apt-get install --no-install-recommends -y \ build-essential \ diff --git a/requirements/base.txt b/requirements/base.txt index a33ee0a32..d49c01f7e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -12,7 +12,7 @@ psycopg2==2.9.5 # Images django-inline-svg==0.1.1 -selenium==4.5.0 +selenium==4.7.0 # Markdown verto==1.0.1 @@ -20,14 +20,14 @@ Pygments==2.13.0 python-markdown-math==0.6 # XML Parsing -lxml==4.9.1 +lxml==4.9.2 cssselect==1.2.0 # YAML Loading PyYAML==5.4.1 # I18n -django-modeltranslation==0.18.5 +django-modeltranslation==0.18.7 uniseg==0.7.2 python-bidi==0.4.2 django-bidi-utils==1.0 diff --git a/requirements/local.txt b/requirements/local.txt index 6766c21df..72b872091 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -3,4 +3,4 @@ -r test.txt # Debugging Tools -django-debug-toolbar==3.7.0 +django-debug-toolbar==3.8.1