diff --git a/About.md b/About.md index cb82282..08919d7 100644 --- a/About.md +++ b/About.md @@ -2,6 +2,6 @@ MarkUs is a web application for the submission and grading of student programming assignments. The primary purpose of MarkUs is to provide TAs with simple tools that will help them to give high quality feedback to students. MarkUs also provides a straight-forward interface for students to submit their work, form groups, and receive feedback. The administrative interface allows instructors to manage groups, organize the grading, and release grades to students. -Since 2008, more than 120 undergraduate students have participated in the development of MarkUs; some as full-time summer interns, but most working part time on MarkUs as a project course. The fact that we have have uncovered so few major bugs, and that MarkUs has been so well-received by instructors is a testament to the high quality work of these students. MarkUs is used in more than a dozen courses at the University of Toronto, in several courses at the University of Waterloo, and at Γ‰cole Centrale Nantes (in French). +Since 2008, more than 120 undergraduate students have participated in the development of MarkUs; some as full-time summer interns, but most working part-time on MarkUs as a project course. The fact that we have uncovered so few major bugs, and that MarkUs has been so well-received by instructors is a testament to the high quality work of these students. MarkUs is used in more than a dozen courses at the University of Toronto, in several courses at the University of Waterloo, and at Γ‰cole Centrale Nantes (in French). MarkUs is written using Ruby on Rails, and stores student submissions in Git repositories. diff --git a/Admin-Guide.md b/Admin-Guide.md index 3f89895..6d09cf9 100644 --- a/Admin-Guide.md +++ b/Admin-Guide.md @@ -25,7 +25,7 @@ From the MarkUs Administration home page, you can view a list of dashboards. The ### Resque -The Resque dashboard allows you to easily monitor monitor queues, jobs, and workers for MarkUs' background jobs. To access this dashboard click on the `Resque dashboard` link. +The Resque dashboard allows you to easily monitor queues, jobs, and workers for MarkUs' background jobs. To access this dashboard click on the `Resque dashboard` link. ![Resque Link](images/markus-admin-resque-link.png) @@ -35,13 +35,13 @@ This tool allows you to monitor the status of each worker, display information a ### Rails Performance -The Rails Performance dashboard allows you to monitor and visualize information regarding request traffic and MarkUs' response times. To enable this dashboard, in the MarkUs [settings yaml file](Configuration.md#markus-settings) ensure you set the `enabled` setting under `rails_performance` to true. Once you have confirmed the MarkUs server was started with this setting enabled, log in to the MarkUs Administration home page. You will see a link to the `Rails Performance dashboard`. +The Rails Performance dashboard allows you to monitor and visualize information regarding request traffic and MarkUs' response times. To enable this dashboard, in the MarkUs [settings YAML file](Configuration.md#markus-settings) ensure you set the `enabled` setting under `rails_performance` to true. Once you have confirmed the MarkUs server was started with this setting enabled, log in to the MarkUs Administration home page. You will see a link to the `Rails Performance dashboard`. ![Rails Performance Link](images/markus-admin-rails-performance-link.png) ![Rails Performance Dashboard](images/markus-admin-rails-performance-dashboard.png) -This tool allows you to monitor and visualize information surrounding each request that is made to MarkUs. You can view the time it took for MarkUs to respond to each request, how many requests per minute MarkUs recieves, any 500 errors that occurred and the most common requests MarkUs recieves. This dashboard is currently supported through the `rails_performance` gem. For more information about what you can access see the Rails Performance [homepage](https://github.com/igorkasyanchuk/rails_performance). +This tool allows you to monitor and visualize information surrounding each request that is made to MarkUs. You can view the time it took for MarkUs to respond to each request, how many requests per minute MarkUs receives, any 500 errors that occurred and the most common requests MarkUs receives. This dashboard is currently supported through the `rails_performance` gem. For more information about what you can access see the Rails Performance [homepage](https://github.com/igorkasyanchuk/rails_performance). ## Managing Courses @@ -60,11 +60,11 @@ In order to access a specific course you may do so by navigating to the list of ![Markus Course View Link](images/markus-admin-go-to-course-link.png) Doing so will take you to the course's dashboard page where you can view and access the course as if you were an instructor. This is accomplished by giving you an "AdminRole" for the course. This role is created automatically for you when you try and access a course via the UI for the first time. If you wish to create, view or update this role manually, please see the relevant API [documentation](RESTful-API.md#get-apicoursescourse_idroles). -> :spiral_notepad: **NOTE:** Only you have the ability to view and access the Admin role. Instructors cannot see that you have this role when using either the UI or API. +> πŸ—’οΈ **NOTE:** Only you have the ability to view and access the Admin role. Instructors cannot see that you have this role when using either the UI or API. ### Creating and editing a course -In order to create a course, click on the "Create Course" link located at the top right corner of the page. +In order to create a course, click on the "Create Course" link located in the top right corner of the page. ![Markus Course New Link](images/markus-admin-course-new-link.png) @@ -74,11 +74,11 @@ This will redirect you to a page where you can specify the following course prop - **Name**: The name or course code for this course. Please ensure this is correct before creating the course as this cannot be edited later. - **Display Name**: A longer course name or title for users to see. -- **Autotester URL**: A URL that points to the API of a running [Automated Tester](https://github.com/MarkUsProject/markus-autotesting) instance. Setting this will enable automated testing for this course. To disable automated testing, set this to the empty string. - **Maximum File Size (bytes)**: The maximum file size (in bytes) that users are allowed to upload to MarkUs. -- **Course Visibility**: Selecting "hidden" will hide the course from students in a course. Graders and instructors for the course can still see and manage the course as usual. +- **Autotester URL**: A URL that points to the API of a running [Automated Tester](https://github.com/MarkUsProject/markus-autotesting) instance. Setting this will enable automated testing for this course. To disable automated testing, set this to the empty string. +- **Course Visibility**: Selecting "Hidden" will hide the course from students in a course. Graders and instructors for the course can still see and manage the course as usual. -After clicking "Save", the course will be created and you will be taken back to the list of all courses. +After clicking "Save", the course will be created, and you will be taken back to the list of all courses. If you later wish to modify the properties of a course you can reach any course's edit page by going to the list of all courses and clicking on the "edit" action of the course you wish to modify. @@ -109,7 +109,7 @@ This will take you to a page that lists data about every admin and end user. ### Creating and Editing Users -In order to create a new user, click on the "Create User" link located at the top right corner of the page. +In order to create a new user, click on the "Create User" link located in the top right corner of the page. ![Markus User New Link](images/markus-admin-user-new-link.png) @@ -136,7 +136,7 @@ This will take you to the user's edit page, where you can update the same proper ### Bulk Uploading of End Users -If you plan on creating many end users, you may find it convenient to instead upload a list of users for markus to create. To do this, click on the "Upload End Users" link located at the top right corner of the page. +If you plan on creating many end users, you may find it convenient to instead upload a list of users for markus to create. To do this, click on the "Upload End Users" link located in the top right corner of the page. ![Markus User CSV Upload Link](images/markus-admin-user-csv-upload-link.png) @@ -161,7 +161,7 @@ username_3,last_name_3,first_name_3,id_number_3,email_3 ``` After you have selected a file to upload, click on the "Upload" button on the modal. MarkUs will then create End Users from the list of users you provided. The users administration page will be refreshed after all users. -> :spiral_notepad: **Note:** +> πŸ—’οΈ **NOTE:** > > - Only CSV files are currently supported with this feature. > - Any errors such as a missing username, first name or last name will result in the entire upload to fail. diff --git a/Configuration.md b/Configuration.md index e30159b..20c3f46 100644 --- a/Configuration.md +++ b/Configuration.md @@ -12,6 +12,7 @@ echo 'puts JSON.parse(Settings.to_json).to_yaml' | NO_SCHEMA_VALIDATE=1 NO_INIT_ ``` By specifying `NO_SCHEMA_VALIDATE` an error will not be raised if a required key is missing. + By specifying `NO_INIT_SCHEDULER` an error will not be raised if MarkUs can't connect to a redis instance (not required for this task). ## Settings @@ -20,7 +21,10 @@ By specifying `NO_INIT_SCHEDULER` an error will not be raised if MarkUs can't co All values under the `rails:` key are used to set the `Rails.configuration` object when the app starts. -For example, the `queue_adapter` option sets `Rails.configuration.queue_adapter`, and `asset_host` sets `Rails.configuration.action_mailer.asset_host`. For full details see [Rails Guides](https://guides.rubyonrails.org/configuring.html) +> ▢️ **EXAMPLE:** The `queue_adapter` option sets `Rails.configuration.queue_adapter`, +> and `asset_host` sets `Rails.configuration.action_mailer.asset_host`. +> +> For full details see [Rails Guides](https://guides.rubyonrails.org/configuring.html) The default values for these should be good enough for most applications. @@ -35,7 +39,7 @@ rails: log_level: # log level (one of: debug info warn error fatal unknown) cache_store: # cache store name (redis_cache_store is recommended because MarkUs already uses redis elsewhere) active_job: - queue_adapter: # queue adapter name (supported by ActiveJob::QueueAdapters) (resque is recommended because MarkUs already uses redis elsewhere) + queue_adapter: # queue adapter name (supported by ActiveJob::QueueAdapters) (Resque is recommended because MarkUs already uses redis elsewhere) assets: prefix: # relative path from the rails root to write compiled assets to active_record: @@ -51,7 +55,7 @@ rails: perform_deliveries: # boolean indicating whether to send mail or not deliver_later_queue_name: # name of queue used to send mail as a background job sendmail_settings: # (required if delivery_method == sendmail) hash containing sendmail settings - smtp_settings: # (required if delivery_method == smtp) hash containing smtp settings + smtp_settings: # (required if delivery_method == SMTP) hash containing SMTP settings file_settings: # (required if delivery_method == file) hash containing file settings active_support: deprecation: # string indicating where to write deprecation warnings (See ActiveSupport::Deprecation::Behavior for details) @@ -94,7 +98,7 @@ student_csv_order: # column order of student csv upload file (choices are: user_ jupyter_server: hosts: # list of host names of servers running jupyterhub that are allowed to connect to this instance of MarkUs repository: - type: # repository type used to store student submissions. Choose from 'git', 'mem'. 'git' is preferred since 'mem' is not persistant and should only be used for testing. + type: # repository type used to store student submissions. Choose from 'git', 'mem'. 'git' is preferred since 'mem' is not persistent and should only be used for testing. url: # base url used to remotely access a repository over http/https ssh_url: # (required if enable_key_storage == true) base url used to remotely access a repository over ssh is_repository_admin: # boolean indicating whether MarkUs manages repositories @@ -124,11 +128,11 @@ rails_performance: enabled: # boolean whether to enable the rails performance dashboard (See the "Admin Guide" page for more information about this dashboard) duration: # duration in minutes for rails performance to store data for monitoring exception_notification: - enabled: # boolean indicating whether to enable email notifactions when errors occur (See "Error Notification Emails" below for more details) + enabled: # boolean indicating whether to enable email notifications when errors occur (See "Error Notification Emails" below for more details) sender: # email address string with which to email error notifications sender_display_name: # sender display name for recipients to see email_prefix: # string text to prefix to the error subject line that summarizes the error - recipients: # list of string email addresses who will recieve error notification emails + recipients: # list of string email addresses who will receive error notification emails file_storage: default_root_path: # absolute path to a directory where MarkUs can write and store files scanned_exams: # (optional) absolute path to a directory where MarkUs can store scanned exam files (if null, a subdirectory under the default_root_path will be used) @@ -140,16 +144,16 @@ file_storage: ## Additional queue names -By default, background jobs will be run using the queue specified by the +By default, background jobs will be run using the queue specified by the following setting. ```yaml queue: default: ``` -setting. If you would like to use different queue names for different background jobs, you can specify additional keys (the background job name written in snake case) under the `queue:` key. +If you would like to use different queue names for different background jobs, you can specify additional keys (the background job name written in snake case) under the `queue:` key. -For example, the following conifguration: +For example, the following configuration: ```yaml queue: @@ -229,7 +233,7 @@ Additionally, MarkUs can be set to restrict remote logins based on username and/ ### Logout redirect -The `logout_redirect` setting determines where the user will be redirected when they logout of MarkUs. It can be one of `DEFAULT`, `NONE`, or a URL. +The `logout_redirect` setting determines where the user will be redirected when they log out of MarkUs. It can be one of `DEFAULT`, `NONE`, or a URL. - `DEFAULT`: the user will be redirected to MarkUs' login page - `NONE`: MarkUs will render a 404 error page @@ -237,7 +241,7 @@ The `logout_redirect` setting determines where the user will be redirected when ## Environment variables -All of the settings described above can also be set using environment variables. Environment variables start with `MARKUS__` followed by each nested yaml key separated by `__`. For example, +All the settings described above can also be set using environment variables. Environment variables start with `MARKUS__` followed by each nested YAML key separated by `__`. For example, ```sh MARKUS__REDIS__URL=redis://localhost:6379/1 @@ -260,15 +264,15 @@ RAILS_RELATIVE_URL_ROOT=/csc108 bundle exec rails server ## Error Notification Emails -If you wish to be informed when a user encounters a server error whilst using MarkUs, you can configure MarkUs to send you an email whenever such an error event happens along with its details. To do so, under the `exception_notification` settings, set the `enabled` setting to true. Be sure to then specify a `sender` email address and a list of `recipients` addresses. You can also optionally set a `sender_display_name` and an `email_prefix`. +If you wish to be informed when a user encounters a server error whilst using MarkUs, you can configure MarkUs to email you whenever such an error event happens along with its details. To do so, under the `exception_notification` settings, set the `enabled` setting to true. Be sure to then specify a `sender` email address and a list of `recipients` addresses. You can also optionally set a `sender_display_name` and an `email_prefix`. -Note that in order for this feature to work, you **must** have ActionMailer [configured](https://guides.rubyonrails.org/action_mailer_basics.html) to send emails. This means that you must select an ActionMailer `delivery_method` with the appropriate settings and you must also set `perform_deliveries` to true. You will be unable to send or recieve error notification emails otherwise. +Note that in order for this feature to work, you **must** have ActionMailer [configured](https://guides.rubyonrails.org/action_mailer_basics.html) to send emails. This means that you must select an ActionMailer `delivery_method` with the appropriate settings, and you must also set `perform_deliveries` to true. You will be unable to send or receive error notification emails otherwise. -This feature informs you of all uncaught exceptions that occur in the MarkUs backend. In order to possibly avoid filling recipient inboxes with a lot of the same error notifications, email notifications are sent after every `2**n` occurences of the same error. For more details, visit the [exception notification](https://github.com/smartinez87/exception_notification) gem homepage with which we use to provide you this feature. +This feature informs you of all uncaught exceptions that occur in the MarkUs backend. In order to possibly avoid filling recipient inboxes with a lot of the same error notifications, email notifications are sent after every `2**n` occurrences of the same error. For more details, visit the [exception notification](https://github.com/smartinez87/exception_notification) gem homepage with which we use to provide you this feature. ## LTI Settings ->**Note**: LTI routes are not enabled in production by default. To enable them, you must edit `routes.rb` file. +> πŸ—’οΈ **Note**: LTI routes are not enabled in production by default. To enable them, you must edit `routes.rb` file. If you wish to use Learning Tools Interoperability (LTI) with MarkUs, you'll need to configure the LTI settings as follows @@ -276,7 +280,7 @@ If you wish to use Learning Tools Interoperability (LTI) with MarkUs, you'll nee - `lti.token_endpoint` must be the url used to generate an LTI credentials token for the external platform. - `lti.sync_schedule` must be a cron schedule dictating when MarkUs should attempt to automatically sync its roster via LTI. -You must also create a private key for generating Javascript Web Tokens to sign LTI requests. +You must also create a private key for generating JavaScript Web Tokens to sign LTI requests. A private key can be automatically created with the `markus:lti_key` rake task. If you wish to filter course creation requests from LTI deployments, add the following keys: diff --git a/Developer-Guide--Action-Policy-Style-Guide.md b/Developer-Guide--Action-Policy-Style-Guide.md index 0e8faea..8a75b59 100644 --- a/Developer-Guide--Action-Policy-Style-Guide.md +++ b/Developer-Guide--Action-Policy-Style-Guide.md @@ -16,7 +16,7 @@ Policy files can be found under: `app/policies` Policy translation files can be found under: `config/locales/policies` -Rspec tests can be found under: `spec/policies` +rspec tests can be found under: `spec/policies` ## Guidelines for writing policies @@ -30,7 +30,7 @@ MarkUs tries to use [resourceless authorization](https://actionpolicy.evilmartia Instead, we can define an [`implicit_authorization_target`](https://actionpolicy.evilmartians.io/#/behaviour?id=implicit-authorization-target) method for each controller. -By default, controllers will inherit this method from `ApplicationController` but in some cases it may be necessary to override this method for a subclass. For example, if a controller does not have a corresponding model, the default `implicit_authorization_target` will need to be overwritten since it assumes the existance of that model. +By default, controllers will inherit this method from `ApplicationController` but in some cases it may be necessary to override this method for a subclass. For example, if a controller does not have a corresponding model, the default `implicit_authorization_target` will need to be overwritten since it assumes the existence of that model. ### Policy names should reflect their purpose @@ -96,7 +96,7 @@ class ExamplePolicy < ApplicationPolicy end ``` -If the `index?` policy fails the error messages will be: `["You don't have access to the index route.", "You are not an instructor user"]` becuase both the `index?` and `instructor?` policies are called. +If the `index?` policy fails the error messages will be: `["You don't have access to the index route.", "You are not an instructor user"]` because both the `index?` and `instructor?` policies are called. Scenario 2: @@ -160,7 +160,7 @@ allowed_to?(:index?, context: { submission: Submission.find(10) }) ### Writing Tests -Rspec tests written for policies should use [Action Policies' Rspec DSL](https://actionpolicy.evilmartians.io/#/testing?id=rspec-dsl). +rspec tests written for policies should use [Action Policies' rspec DSL](https://actionpolicy.evilmartians.io/#/testing?id=rspec-dsl). For clarity, we prefer to not nest `succeed` or `failed` blocks within each other. For example, the following two test classes are functionally equivalent but the second one is preferred: diff --git a/Developer-Guide--Configure-Environment-to-use-VSCode.md b/Developer-Guide--Configure-Environment-to-use-VSCode.md index f08655c..2adf51e 100644 --- a/Developer-Guide--Configure-Environment-to-use-VSCode.md +++ b/Developer-Guide--Configure-Environment-to-use-VSCode.md @@ -8,7 +8,7 @@ Install the dev containers [extension](https://marketplace.visualstudio.com/item ### Create a `devcontainer` Configuration -Devcontainer files define the vscode environment inside a docker container. It allows us to configure VSCode extensions, environment definitions, etc when running VSCode inside the docker container. +Devcontainer files define the VS Code environment inside a docker container. It allows us to configure VSCode extensions, environment definitions, etc when running VSCode inside the docker container. Place the following file: `devcontainer.json` at the root of your project inside a `.devcontainer` folder. @@ -55,15 +55,15 @@ Place the following file: `devcontainer.json` at the root of your project inside Brief explanation of what is happening above: -- On startup, let's open up VSCode inside the `/app` folder of our appplication by defining our workspace folder `workspaceFolder` to point to the `/app` directory of our container. +- On startup, let's open up VSCode inside the `/app` folder of our application by defining our workspace folder `workspaceFolder` to point to the `/app` directory of our container. - We are installing `ruby-lsp` and `gitlens`, both VSCode extensions inside the dev container and specifying their configuration, such as ignoring certain folders from indexing specific paths. Notice that when we open up markus outside the dev container, these extensions will be absent. -- Force the `listen` gem to poll for changes by setting the `LISTEN_POLLING` flag. This removes flakyness in our autoreloading. +- Force the `listen` gem to poll for changes by setting the `LISTEN_POLLING` flag. This removes flakiness in our auto reloading. ### Enable the `ruby-lsp` Gem -To enable modern programming features such as `go-to`, `code completion`, etc, an LSP server is required, which the default RubyMine IDE already comes preconfigured with. To work eficiently in VSCode we must enable the optionally defined `ruby-lsp` gem. +To enable modern programming features such as `go-to`, `code completion`, etc, an LSP server is required, which the default RubyMine IDE already comes preconfigured with. To work efficiently in VSCode we must enable the optionally defined `ruby-lsp` gem. -To install optional gem groups, we must pass in the `BUNDLE_WITH` enviroment variable with the optional groups we wish to install. +To install optional gem groups, we must pass in the `BUNDLE_WITH` environment variable with the optional groups we wish to install. Inside the `docker-compose.override.yml`, we must define the following environment variable: @@ -77,4 +77,4 @@ When must then run: `docker compose run --rm deps-updater` to install the depend ## Execution -To run your code inside the dev container extension open up the command palette, either by pressing down `cmd + Shift + P` (on MAC OS) or by going to `view > Command Palette` and typing in `Dev Containers: Reopen in Container` +To run your code inside the dev container extension open up the command palette, either by pressing down `cmd + Shift + P` (on macOS) or by going to `view > Command Palette` and typing in `Dev Containers: Reopen in Container` diff --git a/Developer-Guide--Guidelines.md b/Developer-Guide--Guidelines.md index 97f5901..2f93ffc 100644 --- a/Developer-Guide--Guidelines.md +++ b/Developer-Guide--Guidelines.md @@ -20,16 +20,16 @@ When developing MarkUs, make sure to follow the following steps (where appropria - **Do** use Rails tools (such as [generators](http://wiki.rubyonrails.org/rails/pages/AvailableGenerators)) when appropriate in order to have code-stubs generated and for [migrations](http://guides.rubyonrails.org/migrations.html) - **Do** use the [debugger](http://guides.rubyonrails.org/debugging_rails_applications.html). -- **Do** document your code appropriately. Add or update method-level and class-level docstrings as required. You can assume that the reader is familiar with Ruby and Rails. If your code requires more extensive documentation, you may wish to add or update a Wiki page. Remember, once you are done with your work and you leave the project, new developers should be able to use what you have contributed without a lot of effort. +- **Do** document your code appropriately. Add or update method-level and class-level docstrings as required. You can assume that the reader is familiar with Ruby and Rails. If your code requires more extensive documentation, you may wish to add or update a Wiki page. Remember, once you are done with your work, and you leave the project, new developers should be able to use what you have contributed without a lot of effort. - **Do** ask for help. Ask questions commenting on your pull requests, emailing or in talking to the maintainers in person (if you can). But don't wait until the project is almost finished; problems can often be resolved quickly by sharing your code and asking questions. - **Do** write tests! (see the [guides](#guides) for testing guides) ### DON'Ts -1. **Don't** mark your pull request as "Ready for review" until you're entirely happy with it. Go, have a break and come back to your code after a while. Questions you should ask yourself are: Is my controller code really controller code, or should it be moved to a model? Is there a simpler solution? Can Rails help with what I am trying to achieve? -2. **Don't** mull over problems alone for hours/days. Sometimes it's better to consult somebody else: two pairs of eyes see more than one. Maybe somebody else has had a similar problem, etc. Go ask questions! -3. **Don't** fight Rails (it'll beat you). Sometimes Rails' "magic" is irritating. However, you are better off *using* rather then fighting it! -4. **Don't** use absolute paths/url's in any code (use `url_for` instead). *Always* let Rails generate URLs. You have to assume that there are more than one MarkUs applications running on a server, once deployed. Rails does a really good job on this, so use it. +- **Don't** mark your pull request as "Ready for review" until you're entirely happy with it. Go, have a break and come back to your code after a while. Questions you should ask yourself are: Is my controller code really controller code, or should it be moved to a model? Is there a simpler solution? Can Rails help with what I am trying to achieve? +- **Don't** mull over problems alone for hours/days. Sometimes it's better to consult somebody else: two pairs of eyes see more than one. Maybe somebody else has had a similar problem, etc. Go ask questions! +- **Don't** fight Rails (it'll beat you). Sometimes Rails' "magic" is irritating. However, you are better off *using* rather than fighting it! +- **Don't** use absolute paths/URL's in any code (use `url_for` instead). *Always* let Rails generate URLs. You have to assume that there are more than one MarkUs applications running on a server, once deployed. Rails does a really good job on this, so use it. ## Code Styleguide @@ -52,7 +52,7 @@ Rails: - - -Rspec: +rspec: - - diff --git a/Developer-Guide--Set-Up-With-Docker.md b/Developer-Guide--Set-Up-With-Docker.md index 4123f27..c46b640 100644 --- a/Developer-Guide--Set-Up-With-Docker.md +++ b/Developer-Guide--Set-Up-With-Docker.md @@ -8,7 +8,7 @@ If you want to get started on working on MarkUs quickly and painlessly, this is - If you are given a choice of which operating system to use, select *Ubuntu 22.04*. -2. If you are using **Windows** or **MacOS** you will need to install Docker by following the instructions on [this page](https://docs.docker.com/get-docker/). If you are using **Linux**, you will need to install [Docker Engine](https://docs.docker.com/engine/install/). (On Linux, Docker Desktop is known to cause issues with MarkUs, so it is important that you install Docker Engine and not Docker Desktop. If you already have Docker Desktop installed, see Q6.) +2. If you are using **Windows** or **macOS** you will need to install Docker by following the instructions on [this page](https://docs.docker.com/get-docker/). If you are using **Linux**, you will need to install [Docker Engine](https://docs.docker.com/engine/install/). (On Linux, Docker Desktop is known to cause issues with MarkUs, so it is important that you install Docker Engine and not Docker Desktop. If you already have Docker Desktop installed, see Q6.) - On Windows, make sure you've selected the "WSL 2 backend" tab under "System Requirements" and follow those instructions. - On Linux, also follow the instructions on "Manage Docker as a non-root user" [here](https://docs.docker.com/install/linux/linux-postinstall/). @@ -115,9 +115,9 @@ We strongly recommend RubyMine (a JetBrains IDE) for all MarkUs development. If 3. Complete the setup steps under [Docker: Enable Docker Support JetBrains guide](https://www.jetbrains.com/help/ruby/docker.html#enable_docker). -4. To configure RubyMine to use a remote Ruby interpreter from the Docker image: [JetBrains guide](https://www.jetbrains.com/help/ruby/configuring-language-interpreter.html#add_remote_ruby_interpreter). Use `rails` as the service. After you've selected this interpreter, RubyMine will take some time to index all of the Ruby gems (libraries); you'll see "Indexing"... at the bottom of the RubyMine window. +4. To configure RubyMine to use a remote Ruby interpreter from the Docker image: [JetBrains guide](https://www.jetbrains.com/help/ruby/configuring-language-interpreter.html#add_remote_ruby_interpreter). Use `rails` as the service. After you've selected this interpreter, RubyMine will take some time to index all Ruby gems (libraries); you'll see "Indexing"... at the bottom of the RubyMine window. - If this doesn't work, please make sure you're using the latest version of RubyMine (Help -> Check for Updates...). + If this doesn't work, please make sure you're using the latest version of RubyMine (Help β†’ Check for Updates...). 5. To configure RubyMine to connect to the PostgreSQL database that MarkUs uses for development, first make sure the MarkUs server is running (by doing a `docker compose run...` as above). Then follow [these instructions](https://www.jetbrains.com/help/idea/running-a-dbms-image.html#6aa07130) in RubyMine to connect to the PostgreSQL server running as the 'postgres' docker-compose service. Note that you do not need to create a new container so you should only need to follow the instructions under "Connect to the PostgreSQL server". @@ -132,8 +132,8 @@ We strongly recommend RubyMine (a JetBrains IDE) for all MarkUs development. If We use [pre-commit](https://pre-commit.com/) to run automated checks on code before each commit. To set this up on your local computer (*not* in a Docker container): 1. First, install Python 3. -2. Then, install the pre-commit library: `$ python3 -m pip install pre-commit` (or just `python` instead of `python3`, depending on your Python executable. Homebrew Python might [block](https://peps.python.org/pep-0668/) the above command. If this fails, run `brew install pipx` -> `pipx install pre-commit`. -3. Finally, in the `Markus` folder run `$ pre-commit install`. This will install all of the Markus pre-commit hooks. +2. Then, install the pre-commit library: `$ python3 -m pip install pre-commit` (or just `python` instead of `python3`, depending on your Python executable). Homebrew Python might [block](https://peps.python.org/pep-0668/) the above command. If this fails, run `brew install pipx` -> `pipx install pre-commit`. +3. Finally, in the `Markus` folder run `$ pre-commit install`. This will install all Markus pre-commit hooks. After this, these checks will run every time you make a commit. If all checks pass, the commit will proceed as normal. If a check fails, the commit *does not* occur, and there are two possibilities: @@ -197,11 +197,11 @@ If you need to rebuild the MarkUs docker image: 9. Now, open a shell in the MarkUs docker container: `docker compose run --rm rails bash`. 10. Execute the following commands in the MarkUs container. 1. Create sample autotesting assignments: `rails db:autotest`. - 2. (*The MarkUs server and autotest containers be running when you run these commands.*) Run tests for every sample autotesting asignment: `MARKUS_URL= rails db:autotest_run`, where `` is in the form `http://:3000`, and `` is the domain you used in Step 7 (e.g., `host.docker.internal`). + 2. (*The MarkUs server and autotest containers be running when you run these commands.*) Run tests for every sample autotesting assignment: `MARKUS_URL= rails db:autotest_run`, where `` is in the form `http://:3000`, and `` is the domain you used in Step 7 (e.g., `host.docker.internal`). If you get an error when running this command, see "Running tests manually" below. -Now when you visit MarkUs in the web browser, you should see the new assignments that were created, the autotest settings (under Settings -> Automated Testing), and a sample submission with autotest results. +Now when you visit MarkUs in the web browser, you should see the new assignments that were created, the autotest settings (under Settings β†’ Automated Testing), and a sample submission with autotest results. ### Running tests manually @@ -221,7 +221,7 @@ If you plan on doing work that involves sending/receiving emails from MarkUs, yo ## Troubleshooting -**Note: This is an archive of problems related to Docker that are encountered by students, and their solutions.** +> πŸ—’οΈ **NOTE:** This is an archive of problems related to Docker that are encountered by students, and their solutions.** ### Q1 @@ -229,7 +229,7 @@ I'm writing frontend code. The files I've changed should according to the Webpac 1. My changes are valid and should be displayed from the URL I'm accessing. 2. There are no errors in the webpack container's logs. -3. If I run `npm run build-dev` in the webpack container's console directly, it succeeds and I'm able to see my changes afterwards. +3. If I run `npm run build-dev` in the webpack container's console directly, it succeeds, and I'm able to see my changes afterwards. ### A1 @@ -244,11 +244,11 @@ system temporary path is world-writable: /tmp /tmp is world-writable: /tmp . is not writable: /app Exiting -/usr/lib/ruby/3.0.0/tmpdir.rb:39:in `tmpdir': could not find a temporary directory (ArgumentError) +/usr/lib/ruby/3.0.0/tmpdir.rb:39:in `tmpdir`: could not find a temporary directory (ArgumentError) [...stacktrace] ``` -after following the setup guide step by step. I've looked into my host setup and confirmed that my `/tmp`'s permissions are correct (i.e. on Linux you can expect a 1777, on mac it might be a symbolic link to `/private/tmp`, latter of which would also be a 1777). For the second warning/error, I've found that my Markus container's `/app` is owned by `root`, not `markus`. +after following the setup guide step by step. I've looked into my host setup and confirmed that my `/tmp`'s permissions are correct (i.e. on Linux you can expect a 1777, on Mac it might be a symbolic link to `/private/tmp`, latter of which would also be a 1777). For the second warning/error, I've found that my Markus container's `/app` is owned by `root`, not `markus`. ### A2 @@ -273,7 +273,7 @@ Since this is not a wide-spread issue, it's more reasonable to have the setup li ### Q3 -When the `rails` container is started, postgres' database migrations will be auto applied because of the line `bundle exec rails db:prepare` in `entrypoint-dev-rails.sh`. Sometimes migrations fail - sometimes outright when you first start the container with `docker compose up rails`, other times when you successfully create your `rails` container, then make some data change to markus (i.e. adding a new assignment tag) or shut down and restart the `rails` container - like +When the `rails` container is started, postgres database migrations will be auto applied because of the line `bundle exec rails db:prepare` in `entrypoint-dev-rails.sh`. Sometimes migrations fail - sometimes outright when you first start the container with `docker compose up rails`, other times when you successfully create your `rails` container, then make some data change to markus (i.e. adding a new assignment tag) or shut down and restart the `rails` container - like ```MARKDOWN ... @@ -283,7 +283,7 @@ When the `rails` container is started, postgres' database migrations will be aut 2023-09-15 11:47:03 StandardError: An error has occurred, this and all later migrations canceled: 2023-09-15 11:47:03 2023-09-15 11:47:03 PG::DuplicateTable: ERROR: relation "users" already exists -2023-09-15 11:47:03 /app/db/migrate/20080729160237_create_users.rb:3:in `up' +2023-09-15 11:47:03 /app/db/migrate/20080729160237_create_users.rb:3:in `up` 2023-09-15 11:47:03 2023-09-15 11:47:03 Caused by: 2023-09-15 11:47:03 ActiveRecord::StatementInvalid: PG::DuplicateTable: ERROR: relation "users" already exists diff --git a/Developer-Guide--Set-Up-With-Vagrant.md b/Developer-Guide--Set-Up-With-Vagrant.md index 66d5c5f..a000c04 100644 --- a/Developer-Guide--Set-Up-With-Vagrant.md +++ b/Developer-Guide--Set-Up-With-Vagrant.md @@ -11,15 +11,15 @@ If you want to get started on working on MarkUs quickly and painlessly, this is This will download a fairly large (3GB) Debian box from the internet, so go [take a walk](http://news.stanford.edu/news/2014/april/walking-vs-sitting-042414.html) or something. This box has GNOME, PostgreSQL, git, and all of MarkUs’s other dependencies installed. When the download is complete, VirtualBox will run the box in headless mode. -**NOTE:** If, for some reason, it fails and complains about SSH, you most likely have timed out. Check your internet connection attempt to limit network activity to `vagrant up`. +> πŸ—’οΈ **NOTE:** If, for some reason, it fails and complains about SSH, you most likely have timed out. Check your internet connection attempt to limit network activity to `vagrant up`. ## Connecting to your box Next, run `vagrant ssh` to connect to the virtual machine. (If it asks you for a password for vagrant, the password is "vagrant".) To avoid having to enter a password each time, and to use RubyMine, [set up](https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2) a public private key pair, and copy the public key to `~/.ssh/authorized_keys` on the vagrant vm. Then open the VagrantFile on your local machine and add `config.ssh.private_key_path = "ABSOLUTE_PATH"` directly under `config.vm.box = markusproject/ubuntu`. ABSOLUTE_PATH is the path to your private key (E.g. `$HOME/.ssh/id_rsa`). -Note: On Windows you may find that you need to put the private key in the same directory as the Vagrantfile. +> πŸ—’οΈ **NOTE:** On Windows you may find that you need to put the private key in the same directory as the Vagrantfile. -**NOTE:** It is possible to set up the virtual machine to share folders with the host machine, but in our experience, this is too slow to be a good work environment, and sometimes doesn't work at all. If you do want to enable shared folders, you can check out that [vagrant documentation](http://docs.vagrantup.com/v2/synced-folders/). We have found it more effective to work with files locally using RubyMine and deploy/upload to the vagrant box when you want to try things out. +> πŸ—’οΈ**NOTE:** It is possible to set up the virtual machine to share folders with the host machine, but in our experience, this is too slow to be a good work environment, and sometimes doesn't work at all. If you do want to enable shared folders, you can check out that [vagrant documentation](http://docs.vagrantup.com/v2/synced-folders/). We have found it more effective to work with files locally using RubyMine and deploy/upload to the vagrant box when you want to try things out. Finally, run `markus` from the project directory. @@ -27,14 +27,14 @@ You should now be able to access the site from your host machine's browser at `h The default instructor user is `instructor` with any non-empty password. Look at `db/seeds.rb` for other users. -If you are using RubyMine then you should jump down to the set up instructions for RubyMine below before proceeding to the next step. +If you are using RubyMine then you should jump down to the set-up instructions for RubyMine below before proceeding to the next step. ## Using RubyMine 1. Install [RubyMine](https://www.jetbrains.com/ruby/), and then run it. 2. When RubyMine runs, select `Open`, or `File > Open`, and navigate to your cloned MarkUs folder on your local machine. - **NOTE**: RubyMine will tell you that there are missing gems to be installed, it is okay to ignore this. + > πŸ—’οΈ **NOTE:** RubyMine will tell you that there are missing gems to be installed, it is okay to ignore this. 3. Open `File > Settings` (on Windows) or `RubyMine > Preferences` (on OSX) where we will configure some different settings. 1) In `Tools > Vagrant`, set the *Instance folder* to your Markus directory on your local machine and leave the *Provider* as "Default". diff --git a/Developer-Guide--Setting-up-Git.md b/Developer-Guide--Setting-up-Git.md index 234289d..126bd95 100644 --- a/Developer-Guide--Setting-up-Git.md +++ b/Developer-Guide--Setting-up-Git.md @@ -5,8 +5,9 @@ First, you'll need to [install Git](https://git-scm.com/downloads) onto your computer. If you already have Git installed, we recommend [updating to the latest version](https://confluence.atlassian.com/bitbucketserver/installing-and-upgrading-git-776640906.html). -**Important**: if you are developing MarkUs on Windows, you should be using WSL to manage your repository, which means you'll need to [Install Git on WSL](https://docs.microsoft.com/en-us/windows/wsl/tutorials/wsl-git) instead of on Windows directly. -All of the commands on this page should be run in the WSL 2 terminal (e.g., Ubuntu 20.04), not the regular Windows terminal (cmd.exe). +> ❗**Important**: if you are developing MarkUs on Windows, you should be using WSL to manage your repository, which means you'll need to [Install Git on WSL](https://docs.microsoft.com/en-us/windows/wsl/tutorials/wsl-git) instead of on Windows directly. + +All commands on this page should be run in the WSL 2 terminal (e.g., Ubuntu 20.04), not the regular Windows terminal (cmd.exe). ## Overview @@ -59,7 +60,7 @@ Note that the following few paragraphs might be a bit confusing. Hang in there, The steps involved until your code ends up in the main MarkUs repository are the following: -1. Create and switch to a feature branch based on up to date local master branch +1. Create and switch to a feature branch based on up-to-date local master branch ```console git branch issue-1234 @@ -104,7 +105,7 @@ You, as a developer, will be mainly working on the locally cloned MarkUs Git rep ### Keeping Your Local Code Up-To-Date -Also note the dashed arrow coming from the main ("upstream") MarkUs Git repository and pointing to your local clone of your personal MarkUs Github fork. This arrow represents interaction you have to do to keep up-to-date with the authoritative MarkUs repository, which is constantly being updated by other developers on the MarkUs team. (More on how you can do this later.) +Also note the dashed arrow coming from the main ("upstream") MarkUs Git repository and pointing to your local clone of your personal MarkUs GitHub fork. This arrow represents interaction you have to do to keep up-to-date with the authoritative MarkUs repository, which is constantly being updated by other developers on the MarkUs team. (More on how you can do this later.) This subsection describes the steps you need to take to make sure your local repository is up-to-date. Generally, your `master` branch should mirror the contents of `upstream/master`, the master branch of the main MarkUs repository. Remember that **you should be doing all development on local feature branches, NOT your local master branch**! This makes merging as painless as possible. diff --git a/Developer-Guide--Tips-And-Tricks--Annotations.md b/Developer-Guide--Tips-And-Tricks--Annotations.md index f95e921..002f40b 100644 --- a/Developer-Guide--Tips-And-Tricks--Annotations.md +++ b/Developer-Guide--Tips-And-Tricks--Annotations.md @@ -14,7 +14,7 @@ Part way down, Jamie notices that c9doej forgot to free some memory that had bee Jamie presses the submit button, and the lines that Jamie had highlighted glows a different colour than the rest of the source code. When Jamie moves the mouse cursor over the glowing code, a little box pops up to display Jamie's message. -Later on, when all of the assignments have been marked and returned, c9doej logs in to check his grade. Scanning through his code, he sees the glowing lines. He hovers his mouse over the lines and reads Jamie's message. "Of course!", thinks c9doej, "I knew that. Won't make that mistake twice." +Later on, when all assignments have been marked and returned, c9doej logs in to check his grade. Scanning through his code, he sees the glowing lines. He hovers his mouse over the lines and reads Jamie's message. "Of course!", thinks c9doej, "I knew that. Won't make that mistake twice." ## Annotations: The Rules @@ -50,7 +50,9 @@ There are three models that deal with annotations: `AnnotationCategory`, `Annota The code that handles the client-side Annotation behaviour can be found here: +```console /public/javascripts/SourceCodeGlower +``` Inside is a series of files - each file defines a particular JavaScript Class. Here is a description of the files/classes, and how they function with each other: @@ -58,7 +60,7 @@ Inside is a series of files - each file defines a particular JavaScript Class. H The current client-side approach to Syntax Highlighting is something I figured might change over time. We might upgrade the Syntax Highlighter to a newer version for better performance, or change to a different library completely (perhaps a server side one). -Because of the possibility of change, I did my best to decouple the Syntax Highlighter from the Source Code Glower. That way, if we did end up changing the Syntax Highlighter library, all of the necessary changes would have to be made in a single file, and the rest of the system should still be OK. +Because of the possibility of change, I did my best to decouple the Syntax Highlighter from the Source Code Glower. That way, if we did end up changing the Syntax Highlighter library, all necessary changes would have to be made in a single file, and the rest of the system should still be OK. SourceCodeAdapter is an abstract class. Here is a list of the responsibilities for any implementation of SourceCodeAdapter: @@ -66,7 +68,7 @@ SourceCodeAdapter is an abstract class. Here is a list of the responsibilities f - To return an Enumerable collection of SourceCodeLine's (a class that I'll discuss in the next section) from that DOM element, using the method getSourceNodes(). -- Given some DOM element X, to determine whether or not X is in the currently highlighted source code, and to return the DOM element that represents the root of a SourceCodeLine. The method for this is getRootFromSelection(some\_node). This is important for determining which lines are selected after highlighting the source with the mouse cursor. +- Given some DOM element X, to determine whether X is in the currently highlighted source code, and to return the DOM element that represents the root of a SourceCodeLine. The method for this is getRootFromSelection(some\_node). This is important for determining which lines are selected after highlighting the source with the mouse cursor. - To perform any run-time hackery on the syntax highlighter DOM element, using applyMods(). @@ -140,19 +142,19 @@ This class represents the Annotation Label in the client-side memory. Its main r This class is in charge of displaying collections of Annotations on the screen. Annotation Labels are displayed in a dynamically generated DIV that is appended to a parent\_node that is attached to the constructor. This generated DIV is hidden until needed, and is styled with CSS class "annotation\_label\_display". -The two variables LABEL\_DISPLAY\_X\_OFFSET and LABEL\_DISPLAY\_Y\_OFFSET offset where the annotation display appears in relation to the mouse cursor. +The two variables `LABEL\_DISPLAY\_X\_OFFSET and LABEL\_DISPLAY\_Y\_OFFSET` offset where the annotation display appears in relation to the mouse cursor. ##### AnnotationLabelManager.js The AnnotationLabelManager is similar to the SourceCodeLineManager - it stores annotation labels within itself based on annotation\_ids. It has the following methods: -- annotationLabelExists(annotation\_label\_id) - returns true/false based on whether or not an annotation\_label is registered at annotation\_label\_id. +- annotationLabelExists(annotation\_label\_id) - returns true/false based on whether an annotation\_label is registered at annotation\_label\_id. - getAnnotationLabel(annotation\_label\_id) - returns the Annotation Label registered under annotation\_label\_id - addAnnotationLabel(annotation\_label) - interrogates an Annotation Label for its ID, and attempts to add it to the internal collection of Annotation Labels. If an Annotation Label already exists at the given ID, an exception is thrown. -- getAllAnnotationLabels() - returns an array of all of the Annotation Labels +- getAllAnnotationLabels() - returns an array of all Annotation Labels ##### SourceCodeLineAnnotations.js @@ -184,13 +186,13 @@ I'm just going to list off the methods for this object one by one, giving a desc - setRelationships() - replace the collection of source code line / annotation label relationships -- relationshipExists(annotation\_id, line\_num, annotation\_label\_id) - returns true or false based on whether or not a relationship exists between a line\_number, an annotation\_label\_id, and an annotation\_id +- relationshipExists(annotation\_id, line\_num, annotation\_label\_id) - returns true or false based on whether a relationship exists between a line\_number, an annotation\_label\_id, and an annotation\_id - removeRelationship(annotation\_id, line\_num, annotation\_label\_id) - removes the relationship between an annotation\_id, line\_num, and annotation\_label\_id. - getAnnotationLabelsForLineNum(line\_num) - given a line number, return all annotation labels associated with that source code line -- hasAnnotation(line\_num) - returns true/false based on whether or not a source code line has any annotations connected to it +- hasAnnotation(line\_num) - returns true/false based on whether a source code line has any annotations connected to it - hideLabel() - hide the AnnotationLabelDisplayer dynamically generated div @@ -202,7 +204,7 @@ As of this writing, we're still using Syntax Highlighter 1.5.1. Therefore, we ha This behaviour is triggered here: -/app/views/annotations/\_codeviewer.html.erb (NOTE - this may have been moved after this was written...I know there was talk of refactoring the grader out of the annotations controller) +/app/views/annotations/\_codeviewer.html.erb (NOTE - this may have been moved after this was written...I know there was talk of refactoring the grader out of the annotations' controller) Once the dp.SyntaxHighlighter.HighlightAll('code') call is complete, sourceCodeReady() is called. This function is currently in the grader view code here: @@ -220,6 +222,6 @@ Finally, the SourceCodeLineAnnotations object is created using the SourceCodeLin ##### line\_annotations -line\_annotations is called and manipulated by basic Javascript functions: add\_annotation\_label, add\_annotation, remove\_annotation, update\_annotation\_label. +line\_annotations is called and manipulated by basic JavaScript functions: add\_annotation\_label, add\_annotation, remove\_annotation, update\_annotation\_label. And that's how the annotations more or less work. diff --git a/Developer-Guide--Tips-And-Tricks--Enabling-ActionMailer-In-Development.md b/Developer-Guide--Tips-And-Tricks--Enabling-ActionMailer-In-Development.md index e7b1aa6..15d7cce 100644 --- a/Developer-Guide--Tips-And-Tricks--Enabling-ActionMailer-In-Development.md +++ b/Developer-Guide--Tips-And-Tricks--Enabling-ActionMailer-In-Development.md @@ -10,7 +10,7 @@ When using the ActionMailer features of MarkUs, the MarkUs administrators config 1. Create a file in the `config` folder with the name `settings.local.yml`. This file allows you to configure Rails and/or MarkUs settings specifically for your local MarkUs instance. - **Note**: This new file will not be tracked by git. Any settings you configure in this file will not (and should not) be committed. + > πŸ—’οΈ **NOTE:** This new file will not be tracked by git. Any settings you configure in this file will not (and should not) be committed. 2. Ensure your `config/settings.local.yml` file contains the following settings: diff --git a/Developer-Guide--Tips-And-Tricks--Security.md b/Developer-Guide--Tips-And-Tricks--Security.md index 1f1f838..c36ffd5 100644 --- a/Developer-Guide--Tips-And-Tricks--Security.md +++ b/Developer-Guide--Tips-And-Tricks--Security.md @@ -15,11 +15,11 @@ Please see below for MarkUs specific security approaches: ### File Upload - All files uploaded by users should have their filename and filepath sanitized so that files are only written to disk in approved locations. -- Files should be written to disk with minimal permissions (ie. do not make uploaded files executable!) +- Files should be written to disk with minimal permissions (i.e. do not make uploaded files executable!) ### Input Validation -- User input (eg. form fields) should always be validated in both the browser and again when the request is received by the server. +- User input (e.g. form fields) should always be validated in both the browser and again when the request is received by the server. - In the browser, use [client-side form validation](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation). - Use validations on model attributes on the server side. In some cases, additional input validation may be required in controller methods as well. @@ -27,19 +27,19 @@ Please see below for MarkUs specific security approaches: MarkUs uses content security policies (CSP) help prevent XSS and CSS injection attacks and are defined in `content_security_policy.rb` initializer file using Rails' [CSP DSL](https://edgeguides.rubyonrails.org/security.html#content-security-policy). -In some cases, MarkUs depends on external code that violates it's CSP. In these cases, it is may be necessary to temporarily override these policies for specific routes. Avoid this if at all possible by using dependencies that do not violate the CSPs outlined in `content_security_policy.rb` but if you absolutely have to: +In some cases, MarkUs depends on external code that violates it's CSP. In these cases, it may be necessary to temporarily override these policies for specific routes. Avoid this if at all possible by using dependencies that do not violate the CSPs outlined in `content_security_policy.rb` but if you absolutely have to: - Only change the policies for the routes that require it. For example, do not change a policy for all routes in a controller if the change is only necessary for one or two routes. - Change the policies so that they ensure the maximum amount of protection. For example, do not permit content from all https sources if only content from a single url is required. #### Writing code to comply with CSP -MarkUs' CSP do not permit certain elements to appear in the DOM as they pose a security risk. Please see below for alternative (more secure) ways of writing html and js code: +MarkUs' CSP do not permit certain elements to appear in the DOM as they pose a security risk. Please see below for alternative (more secure) ways of writing HTML and js code: -- Do not include inline style attributes in DOM elements. Make sure all styling is done in css files. -- Do not add styles in html files using `