diff --git a/README.html b/README.html index 7d046ac06..d952e302c 100644 --- a/README.html +++ b/README.html @@ -15,7 +15,7 @@ const baseUrl = '/2024'
- + -CATcher:
MarkBind:
RepoSense:
TEAMMATES:
In the Python project, NEWS
entries document contributions so that it can be added into the changelog.
CATcher:
MarkBind:
RepoSense:
TEAMMATES:
In the Python project, NEWS
entries document contributions so that it can be added into the changelog.
CATcher:
MarkBind:
RepoSense:
TEAMMATES:
CATcher:
MarkBind:
RepoSense:
TEAMMATES:
Week | Achievements |
---|---|
2 | Merged PR: Hide 0 issue columns #223 |
4 | Submitted Issue: Refactor filters into its own service #249 |
4 | Submitted Issue: Update Angular version to 11.2.14 #237 |
5 | Merged PR: Remove unused services #238, Merged PR: Remove unused models #253, Merged PR: Remove unused session-fix-confirmation component #250 |
5 | Reviewed PR: Upgrade to angular 11 #252 |
Week | Achievements |
---|---|
2 | Merged PR: Hide 0 issue columns #223 |
4 | Submitted Issue: Refactor filters into its own service #249 |
4 | Submitted Issue: Update Angular version to 11.2.14 #237 |
5 | Merged PR: Remove unused services #238, Merged PR: Remove unused models #253, Merged PR: Remove unused session-fix-confirmation component #250 |
5 | Reviewed PR: Upgrade to angular 11 #252 |
List the aspects you learned, and the resources you used to learn them, and a brief summary of each resource.
Learned how GitHub Actions fits into the development workflow, and how to use it to automate tasks. I used the GitHub Actions documentation to learn about the different types of workflows, how to create and configure workflows, and how to use the different actions available.
Resource: GitHub Actions Documentation
Summary: GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. Make code reviews, branch management, and issue triaging work the way you want.
Resource: GitHub Actions Workflow Syntax
Summary: GitHub Actions uses YAML syntax to define the events, jobs, and steps that make up your workflow. You can create a custom workflow or use a starter workflow template to get started.
List the aspects you learned, and the resources you used to learn them, and a brief summary of each resource.
Learned how GitHub Actions fits into the development workflow, and how to use it to automate tasks. I used the GitHub Actions documentation to learn about the different types of workflows, how to create and configure workflows, and how to use the different actions available.
Resource: GitHub Actions Documentation
Summary: GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. Make code reviews, branch management, and issue triaging work the way you want.
Resource: GitHub Actions Workflow Syntax
Summary: GitHub Actions uses YAML syntax to define the events, jobs, and steps that make up your workflow. You can create a custom workflow or use a starter workflow template to get started.
Week | Achievements |
---|---|
5 | Merged PR: Enhance search performance of algolia plugin #2406 |
Week | Achievements |
---|---|
5 | Merged PR: Enhance search performance of algolia plugin #2406 |
A default package manager for Node.js.
npm install
, npm update
, npm run <scripts>
etc. and how they helped streamline the development process."scripts"
, "dependencies"
and how to manage them.A CSS linter that helps enforce conventions and avoid errors.
stylelintrc.js
file, a configuration object for Stylelint to suit our own needs.A JavaScript library that provides a framework for building command-line interfaces (CLIs) in Node.js applications
A CI/CD platform allowing developers to automate workflows directly within their GitHub repository.
.yml
files in .github/workflow
.A default package manager for Node.js.
npm install
, npm update
, npm run <scripts>
etc. and how they helped streamline the development process."scripts"
, "dependencies"
and how to manage them.A CSS linter that helps enforce conventions and avoid errors.
stylelintrc.js
file, a configuration object for Stylelint to suit our own needs.A JavaScript library that provides a framework for building command-line interfaces (CLIs) in Node.js applications
A CI/CD platform allowing developers to automate workflows directly within their GitHub repository.
.yml
files in .github/workflow
.Week | Achievements |
---|---|
2 | Merged PR: Add whitespace validation #1237 |
2 | Merged PR: Prevent redirection when repo not set #228 |
4 | Merged PR: Update to Angular 11 #252 |
5 | Authored PR (to be reviewed): Add devcontainer support #13 |
5 | Authored PR (to be reviewed): Update Design Page #14 |
6 | Authored PR (to be reviewed): Migrate from TSLint to ESLint #1250 |
6 | Authored PR (to be reviewed): Upgrade to Angular 12 #267 |
Week | Achievements |
---|---|
2 | Merged PR: Add whitespace validation #1237 |
2 | Merged PR: Prevent redirection when repo not set #228 |
4 | Merged PR: Update to Angular 11 #252 |
5 | Authored PR (to be reviewed): Add devcontainer support #13 |
5 | Authored PR (to be reviewed): Update Design Page #14 |
6 | Authored PR (to be reviewed): Migrate from TSLint to ESLint #1250 |
6 | Authored PR (to be reviewed): Upgrade to Angular 12 #267 |
Gradle is a build tool designed specifically to meet the requirements of building Java applications. Once it’s set up, building an application is as simple as running a single command on the command line. Gradle performs well and is also useful for managing dependencies via its advanced dependency management system.
Learned about Gradle through a really helpful tutorial.
I learned how to write basic bash scripts via tutorialspoint, and had to implement batch scripts to perform environmental checks for all files tracked by git, to ensure they end with a newline, no prohibited line endings (\r\n
) are present and no trailing whitespaces are present.
Some interesting bugs were encountered when attempting to use pipes in batch files, particularly one that prevents delayed expansion of variables from being properly evaluated as per usual. This is due to variables not being evaluated in the batch context, as the lines are executed only in the cmd-line context. A more detailed analysis of the bug is done by a user of stackoverflow.
As I explored Codecov to determine why it would intermittently fail for GitHub actions, I developed a greater appreciation for the role of code coverage analysis in ensuring software quality. I found its integration with popular CI/CD platforms to be seamless, making it easier to track and improve code coverage across projects. The visualization tools, such as the sunburst graph and diff coverage reports, were especially helpful in identifying areas that needed more testing attention. Furthermore, learning about Codecov's ability to enforce coverage thresholds and generate pull request comments reinforced the importance of maintaining high-quality test suites.
Vue is a progressive JavaScript framework that simplifies the creation of responsive and efficient web applications. Its reactive data-binding and component-based architecture promote modular programming, resulting in more maintainable and scalable code. Learning about Vue's component-based architecture also expanded my understanding of modular programming and how it can lead to more maintainable and scalable code.
Pug is a templating engine that integrates well with Vue, allowing for cleaner and more concise HTML code with the use of whitespace and indentation for structure. By removing the need for closing tags, Pug attempts to make code more readable and organized. Its support for variables, mixins, and inheritance facilitates code reusability and modular design, improving the overall structure and readability of templates.
Cypress is an end-to-end testing framework that simplifies the process of writing and executing tests for web applications. Its intuitive syntax, real-time reloading, and support for network stubbing improve debugging and development efficiency, emphasizing thorough testing. I found its syntax and API to be intuitive and user-friendly, making the process of writing and executing tests more enjoyable. I was particularly impressed with the real-time reloading feature, which allows for faster debugging and development, simplifying E2E testing.
Bloch’s Builder pattern is a design pattern that simplifies object instantiation in Java, particularly for classes with numerous constructor parameters, as it simplifies the process of object instantiation while maintaining immutability and improving readability. This was a particularly useful design pattern when refactoring the CliArguments.java
class, as it had a large number of constructor parameters, and also required flexible construction as some of the fields were optional. The pattern facilitates immutability and reduces the risk of introducing errors in complex Java classes. Read more about here on Oracle's blog.
Polymorphism is a core object-oriented programming concept in Java that allows objects to adopt multiple forms and behaviors based on their context. It promotes code cleanliness, extensibility, and reduces coupling between components, resulting in more flexible and modular applications that can evolve and scale easily. By leveraging polymorphism, I was able to reduce the amount of logic in the main method of RepoSense.java
, by utilizing RunConfigurationDecider
to return the appropriate RunConfiguration
based on the CliArguments
, where the config can be from getRepoConfigurations()
.
Discrete event simulator (DES) is a method used to model real-world systems that can be decomposed into a set of logically separate processes that autonomously progress through time. This was a design that was well suited for designing a CLI Wizard, as it allows for maintaining of a deque of prompts that to be shown to the user, while also allowing the addition of new prompts into the deque depending on the user's responses.
In RepoSense, a variety of git commands are utilized to get information about the repository. Through undertaking DevOps tasks, I was also exposed to other interesting git commands. Here are some of the interesting ones that I was not aware of before.
git shortlog
- Summarizes git log
output, where each commit will be grouped by author and title. This is used in RepoSense to easily count the commits by the users.
git grep
- A powerful tool that looks for specified patterns in the tracked files in the work tree, blobs registered in the index file, or blobs in given tree objects. Patterns are lists of one or more search expressions separated by newline characters. An empty string as search expression matches all lines. Utilized to write Reposense scripts to perform environmental checks for all files tracked by git, to ensure they end with a newline, no prohibited line endings (\r\n
) are present and no trailing whitespaces are present. Used git docs to learn how to use git grep
properly and what its various flags do.
.mailmap
- If the file .mailmap exists at the top-level of the repository, it can be used to map author and committer names and email addresses to canonical real names and email addresses. This is useful to map multiple authors and commenters and provides a way to share the mapping with all other users of the repository. Used git docs to learn how to configure git mailmap properly.
Researched interesting solutions for free URL shortening, looking into 3 main ways to do it. Read about an in-depth writeup in the -Github issue here.
Week | Achievements |
---|---|
3 | Merged PR: Uncaught error when invalid link is clicked #1239 |
4 | Merged PR: Improve activity dashboard design #233 |
4 | Merged PR: Refactor test cases for Login Component, Session Model and Conflict Model #241 |
4 | Merged PR: Remove markdown.css from test env stylesheets #243 |
4 | Merged PR: Refactor test cases for issue paginator #244 |
4 | Merged PR: Refactor test cases for issue sorter #245 |
4 | Merged PR: Refactor github label constants #246 |
4 | Merged PR: Refactor test cases for search filter #247 |
6 | Reviewed PR: Refactor certain filters into its own service #259 |
6 | Merged PR: Remove test cases for permissions service #260 |
6 | Submitted issue: Improve E2E testing #1251 |
Week | Achievements |
---|---|
3 | Merged PR: Uncaught error when invalid link is clicked #1239 |
4 | Merged PR: Improve activity dashboard design #233 |
4 | Merged PR: Refactor test cases for Login Component, Session Model and Conflict Model #241 |
4 | Merged PR: Remove markdown.css from test env stylesheets #243 |
4 | Merged PR: Refactor test cases for issue paginator #244 |
4 | Merged PR: Refactor test cases for issue sorter #245 |
4 | Merged PR: Refactor github label constants #246 |
4 | Merged PR: Refactor test cases for search filter #247 |
6 | Reviewed PR: Refactor certain filters into its own service #259 |
6 | Merged PR: Remove test cases for permissions service #260 |
6 | Submitted issue: Improve E2E testing #1251 |
Java is a high-level, object-oriented and general-purpose programming language. It is used primary in the backend for RepoSense.
Some of the aspects I have learnt regarding Java:
Week | Achievements |
---|---|
1 | Merged PR: [#2073] Refactor RepoConfigCsvParser::processLine method to avoid arrowhead style code #2080 |
2 | Reviewed PR: [#2003] Suppress Console Warning #2088 |
2 | Reviewed PR: [#1224] Update .stylelintrc.json to check for spacing #2094 |
2 | Submitted Issue: Suggestions on improvement for memory performance regarding Regex matching #2091 |
2 | Submitted Issue: Suggestions for reducing runtime and memory usage for StreamGobbler #2095 |
3 | Submitted Issue: Refactor parser package for greater organisation of classes #2103 |
3 | Merged PR: [#1958] Use syntax coloring for code blocks in docs #2099 |
4 | Merged PR: [#2103] Refactor parser package for greater organisation of classes #2104 |
5 | Merged PR: [#2076] Refactor RepoConfiguration to simplify constructor complexity #2078 |
5 | Submitted Issue: Refactor CliArguments to conform to RepoConfiguration 's Builder Pattern #2117 |
5 | Submitted Issue: Implement Proper Deep Cloning for RepoConfiguration and CliArguments #2119 |
5 | Submitted Issue: Parameter Verification for RepoConfiguration and CliArguments #2121 |
Week | Achievements |
---|---|
1 | Merged PR: [#2073] Refactor RepoConfigCsvParser::processLine method to avoid arrowhead style code #2080 |
2 | Reviewed PR: [#2003] Suppress Console Warning #2088 |
2 | Reviewed PR: [#1224] Update .stylelintrc.json to check for spacing #2094 |
2 | Submitted Issue: Suggestions on improvement for memory performance regarding Regex matching #2091 |
2 | Submitted Issue: Suggestions for reducing runtime and memory usage for StreamGobbler #2095 |
3 | Submitted Issue: Refactor parser package for greater organisation of classes #2103 |
3 | Merged PR: [#1958] Use syntax coloring for code blocks in docs #2099 |
4 | Merged PR: [#2103] Refactor parser package for greater organisation of classes #2104 |
5 | Merged PR: [#2076] Refactor RepoConfiguration to simplify constructor complexity #2078 |
5 | Submitted Issue: Refactor CliArguments to conform to RepoConfiguration 's Builder Pattern #2117 |
5 | Submitted Issue: Implement Proper Deep Cloning for RepoConfiguration and CliArguments #2119 |
5 | Submitted Issue: Parameter Verification for RepoConfiguration and CliArguments #2121 |
I had contributed to CATcher as part of IWM, but I have never really approached the Angular aspects of the project.
Essentially, the core ideas behind Angular involves:
@Component
decorator, an HTML template and styles.
-The other key concepts include event bindings and property binding that link the template to the TypeScript class. Knowing these essentials allowed me to fix WATcher PR#57.
Another key part of Angular is its Dependency Injection system and services. Angular allows us to provide dependencies at different levels of the application, and how the dependencies are instantiated.
Finally, as part of fixing "Remove label-filter-bar as module export #92", I also learned about how related components are organized and grouped into modules. Each Module are self-contained and provide a certain set of functionality and components related to that module, thereby achieving separation of concerns.
Resources:
After having 2 separate hotfixes pushed in a single semester, I started to look more deeply into ensuring the robustness of our application. During these 2 hotfixes, bugs were only uncovered during manual testing. However, it is time consuming to conduct manual tests, and we need to find a way to automate it. E2E tests simulate user interactions such as clicks and typing and is a useful way to ensure our end-product is performing as expected.
During this semester, one of the high priority issues was to migrate our E2E solution away from Protractor. As such, I have investigated Cypress and Playwright as two potential E2E solutions.
When performing migration from Protractor to Playwright, I learned about the different strategies E2E tests can be conducted. Typically, we would want to conduct E2E tests against our production server, since that is what our end users will be using. However, since CATcher depends alot on GitHub's API for its functionality, we are unable to perform automated tests against GitHub. A second strategy would be to mock the functions that hit GitHub's API, and we would test solely the functionalities and behaviours of the app. This let me realized that there is a test vs production version of CATcher.
I have also looked into whether it is possible to perform E2E testing against the production server, since one of the bugs fixed in the hotfixes can only be caught if we did not adopt a mocking strategy. One of the key feasibility concerns I had with testing against the GitHub API was simulating user authentication. This was because authenticating with GitHub requires multi-factor authentication, something that is difficult to achieve with automated E2E testing. Some potential solutions to bypassing MFA would be to use TOTP, which can be generated programmatically. More research will be needed in this area.
Resources:
I also picked up Github Actions when contributing to the CI/CD pipeline in Enable linting in Github workflow #81. I learned how Github Actions are set up and how they can be triggered upon pushing to main/master and also on pull requests.
Furthermore, I learnt how we can use matrix strategies to run the same job with different parameters, such as different OS and different node versions.
Resources:
Part of working with CATcher source code was frequently encountering Observables and Observers. RxJS supports Observers
and Observables
, allowing updates to some Observable
to be received by some Observer
that is subscribed to it. With this pattern, we can trigger updates in many dependent objects automatically and asynchronously when some object state changes.
Resources:
The other key concepts include event bindings and property binding that link the template to the TypeScript class. Knowing these essentials allowed me to fix WATcher PR#57.
Another key part of Angular is its Dependency Injection system and services. Angular allows us to provide dependencies at different levels of the application, and how the dependencies are instantiated.
Finally, as part of fixing "Remove label-filter-bar as module export #92", I also learned about how related components are organized and grouped into modules. Each Module are self-contained and provide a certain set of functionality and components related to that module, thereby achieving separation of concerns.
Resources:
After having 2 separate hotfixes pushed in a single semester, I started to look more deeply into ensuring the robustness of our application. During these 2 hotfixes, bugs were only uncovered during manual testing. However, it is time consuming to conduct manual tests, and we need to find a way to automate it. E2E tests simulate user interactions such as clicks and typing and is a useful way to ensure our end-product is performing as expected.
During this semester, one of the high priority issues was to migrate our E2E solution away from Protractor. As such, I have investigated Cypress and Playwright as two potential E2E solutions.
When performing migration from Protractor to Playwright, I learned about the different strategies E2E tests can be conducted. Typically, we would want to conduct E2E tests against our production server, since that is what our end users will be using. However, since CATcher depends alot on GitHub's API for its functionality, we are unable to perform automated tests against GitHub. A second strategy would be to mock the functions that hit GitHub's API, and we would test solely the functionalities and behaviours of the app. This let me realized that there is a test vs production version of CATcher.
I have also looked into whether it is possible to perform E2E testing against the production server, since one of the bugs fixed in the hotfixes can only be caught if we did not adopt a mocking strategy. One of the key feasibility concerns I had with testing against the GitHub API was simulating user authentication. This was because authenticating with GitHub requires multi-factor authentication, something that is difficult to achieve with automated E2E testing. Some potential solutions to bypassing MFA would be to use TOTP, which can be generated programmatically. More research will be needed in this area.
Resources:
I also picked up Github Actions when contributing to the CI/CD pipeline in Enable linting in Github workflow #81. I learned how Github Actions are set up and how they can be triggered upon pushing to main/master and also on pull requests.
Furthermore, I learnt how we can use matrix strategies to run the same job with different parameters, such as different OS and different node versions.
Resources:
Part of working with CATcher source code was frequently encountering Observables and Observers. RxJS supports Observers
and Observables
, allowing updates to some Observable
to be received by some Observer
that is subscribed to it. With this pattern, we can trigger updates in many dependent objects automatically and asynchronously when some object state changes.
Resources:
GetCourseJoinStatusAction
and PutDataBundleDocumentsAction
FeedbackSessionsDb
Week | Achievements |
---|---|
3 | Merged PR: [#12048] Migrate GetCourseJoinStatusAction #12713 |
4 | Merged PR: [#12048] Migrate PutDataBundleDocumentsAction #12734 |
5 | Merged PR: [#12048] Update restoreDeletedFeedbackSession to save updated entity to db #12751 |
5 | Merged PR: [#12048] Add test cases for FeedbackSessionsDb #12752 |
GetCourseJoinStatusAction
and PutDataBundleDocumentsAction
FeedbackSessionsDb
Week | Achievements |
---|---|
3 | Merged PR: [#12048] Migrate GetCourseJoinStatusAction #12713 |
4 | Merged PR: [#12048] Migrate PutDataBundleDocumentsAction #12734 |
5 | Merged PR: [#12048] Update restoreDeletedFeedbackSession to save updated entity to db #12751 |
5 | Merged PR: [#12048] Add test cases for FeedbackSessionsDb #12752 |
EnrollStudentsAction
, SearchAccountRequestsAction
, AccountRequestSearchIndexingWorkerAction
CoursesLogic
GetFeedbackSessionSubmittedGiverSetAction
and CoursesDb
GetSessionResponseStatsActionIT
AdminNotificationsE2ETest
and AdminSearchPageE2ETest
Week | Achievements |
---|---|
2 | Submitted Issue: Excess padding on edit course details component #12693 |
3 | Merged PR: [#12048] Migrate enroll students action #12715 |
3 | Authored PR: [#12329] Refactoring of sortable tables - notifications table #12717 |
4 | Merged PR: [#12048] Migrate search account requests action #12726 |
5 | Merged PR: [#12048] Add tests for CoursesLogic #12746 |
5 | Merged PR: [#12048] Migrate AccountRequestSearchIndexingWorkerAction #12757 |
6 | Merged PR: [#12048] Fix GetSessionResponseStatsActionIT #12777 |
6 | Merged PR: [#12048] Create IT for GetFeedbackSessionSubmittedGiverSetAction #12778 |
6 | Authored PR: [#12048] Add tests for CoursesDbIT #12786 |
R | Merged PR: [#12048] Migrate Admin Notifications E2E Test #12793 |
R | Authored PR: [#12048] Migrate AdminSearchPageE2ETest SQL #12811 |
R | Merged PR: [#12048] Migrate AdminSearchPageE2ETest #12838 |
EnrollStudentsAction
, SearchAccountRequestsAction
, AccountRequestSearchIndexingWorkerAction
CoursesLogic
GetFeedbackSessionSubmittedGiverSetAction
and CoursesDb
GetSessionResponseStatsActionIT
AdminNotificationsE2ETest
and AdminSearchPageE2ETest
Week | Achievements |
---|---|
2 | Submitted Issue: Excess padding on edit course details component #12693 |
3 | Merged PR: [#12048] Migrate enroll students action #12715 |
3 | Authored PR: [#12329] Refactoring of sortable tables - notifications table #12717 |
4 | Merged PR: [#12048] Migrate search account requests action #12726 |
5 | Merged PR: [#12048] Add tests for CoursesLogic #12746 |
5 | Merged PR: [#12048] Migrate AccountRequestSearchIndexingWorkerAction #12757 |
6 | Merged PR: [#12048] Fix GetSessionResponseStatsActionIT #12777 |
6 | Merged PR: [#12048] Create IT for GetFeedbackSessionSubmittedGiverSetAction #12778 |
6 | Authored PR: [#12048] Add tests for CoursesDbIT #12786 |
R | Merged PR: [#12048] Migrate Admin Notifications E2E Test #12793 |
R | Authored PR: [#12048] Migrate AdminSearchPageE2ETest SQL #12811 |
R | Merged PR: [#12048] Migrate AdminSearchPageE2ETest #12838 |
Gradle is a very flexible build automation tool used for everything from testing and formatting, to builds and deployments. Unlike with other build automation tools like Maven where build scripts written in XML (a widely hated feature of the tool), Gradle build scripts are written in a domain specific language based on Groovy or Kotlin, which are both JVM based languages. This means that it can interact seamlessly with Java libraries.
Gradle is also much more performant than alternatives like Maven because of its:
RepoSense recently added functionality for hot reload on frontend as a Gradle task, which made frontend development a lot more productive. Unfortunately, the feature isn't available on Linux because the package we were using (Apache Ant's condition package) could not specifically check for it. Migrating to Gradle's own platform package recently taken out of incubation, allowed us to support all 3 prominent operating systems.
References:
Like Gradle, Github Actions help with automation of workflows like CI/CD and project management, and can be triggered by a variety of events (pull requests, issues, releases, forks, etc). It also has a growing library of plugins that make workflows a lot easier to set-up. I was surprised that there is some nice tooling support for GitHub actions on IntelliJ.
GitHub actions allows users to run CI on a variety of operating systems, such as Ubuntu XX.04, macOS and Windows Server (which is virtually the same as Windows 10/11 but with better hardware support and more stringent security features).
GitHub also provides a variety of API to interact with these objects. One quirk I came across with the API was that posting single comments on pull requests need to go through the issues endpoint instead of the pulls endpoint (the endpoint for pulls requires code to be referenced). This doesn't cause problems since issues and pulls will never have identical IDs.
GitHub deployment APIs also returns deployment information in pages, which is a sensible thing to do but can cause slight inconvenience when long running PRs have more deployments than can fit in a page.
Actions and APIs also have some great documentation:
Git exploded in popularity in large part due to Git hosting providers like GitHub. GitLab and Bitbucket are also commonly used Git hosts. RepoSense has thus far only largely supported GitHub, but there is a clear incentive to support other commonly used remotes. This is made a little challenging due to differences in conventions between the sites:
base_url | Commit View | Blame View | |
---|---|---|---|
GitHub | github.com | {base_url}/{org}/{repo_name}/commit/{commit_hash} | {base_url}/{org}/{repo_name}/blame/{branch}/{file_path} |
GitLab | gitlab.com | {base_url}/{org}/{repo_name}/-/commit/{commit_hash} | {base_url}/{org}/{repo_name}/-/blame/{branch}/{file_path} |
Bitbucket | bitbucket.org | {base_url}/{org}/{repo_name}/commits/{commit_hash} | {base_url}/{org}/{repo_name}/annotate/{branch}/{file_path} |
For example, Bitbucket uses the term 'annotate' instead of 'blame' because the word 'blame' is insufficiently positive.
In investigating the output of git remote -v
, I noticed there were 2 remotes (fetch and push) for each remote name, which I was confused by. The utility of the separation of fetch and push remotes is for triangular workflows.
We are probably familiar with the common workflow for updating a branch on a forked repo which involves first pulling updates from upstream master, then making changes and pushing to our own fork. This requires remembering to fetch and push to separate repos. With triangular workflows, you can have fetch and push apply to separate repos but with the same remote name, which makes this process much more convenient.
Cypress is a frontend testing tool for testing applications that run in the browser, with tests that are easy to read and write. It uses browser automation (similar to Selenium) and comes with a browser and relevant dependencies out of the box, so it's very easy to set-up. Cypress also provides a dashboard for convenient monitoring of test runs.
https://docs.cypress.io/guides/overview/why-cypress#In-a-nutshell
Bash scripts can be run in a github action workflow, which greatly expands the scope of things you can do with actions. Bash is quite expressive (I hadn't realised just how much it could do). some cool things I learned you could do:
$*
gives parameter values separated by the value in IFS
(Internal File Separator).python3
with a -c
flag and perform more complex processing with a single line python program.ls 1> out 2> err
)Being relatively new to frontend tools, I found Vue.js to be quite interesting. Vue allows code reusability and abstractions through components. While I didn’t work extensively on the frontend, what I learned from the bits that I did work on was quite cool:
Vue state: I found it interesting that you could have computed properties that are accessed the same way as properties, but can depend on other properties and can dynamically update when these properties change. This is often more elegant than using a Vue watcher to update a field. You can even have computed setters that update dependent properties when set. A watcher, however, can be more appropriate when responses to changing data are expensive or need to be done asynchronously.
Vue custom directives: Directives are ways to reuse lower level DOM logic. Directives can define vue life-cycle hooks and later be bound to components (can actually take in any JavaScript object literal). For implementing lazy loads, I needed to use the vue-observe-visibility (external library) directive with slight modification to the hooks to be compatible with Vue3.
References:
Pug is a templating language that compiles to HTML. It is less verbose and much more maintainable than HTML, and also allows basic presentation logic with conditionals, loops and case statements.
There are a lot of these, and most just remain quirks but some result in actual unintended bugs in production (often in edge cases). It was interesting to see this in our contribution bar logic. A technique sometimes used to extract the integer part of a number is to use parseInt
(it's even suggested in a Stack Overflow answer). It turns out we were using this for calculating the number of contribution bars to display for a user. This works for most values, but breaks when numbers become very large or small (less than 10^-7). In this unlikely situation, we'd display anywhere from 1 to 9 extra bars (moral: use Math.floor
instead!).
An investigation into string representations in browsers led me down a rabbit hole of JavaScript runtimes and engines, and ultimately improved my understanding of JavaScript in general. Different browsers have different JS engines - Chrome uses V8, Firefox uses SpiderMonkey (the original JS engine written by Brendan Eich), Edge used to use Chakra but now also uses V8, Safari uses WebKit, etc. Engines often differ significantly in terms of the pipeline for code execution, garbage collection, and more.
The V8 engine as an example, first parses JavaScript into an Abstract Syntax Tree (AST) which is then compiled into bytecode. This bytecode is interpreted by the Ignition interpreter (Ignition also handles compilation of the AST into bytecode). Code that is revisited often during interpretation is marked "hot" and compiled further into highly efficient machine code. This technique of optimising compilation based on run-time profiling (Just-In-Time (JIT) compilation) is also used in other browser engines like SpiderMonkey and the JVM.
The engine is used for running things that are on the browser stack. JS is run in a single thread, and asynchronous tasks are done through callbacks in a task queue. The main script is first run, with things like promises and timeouts inserting tasks into a task queue. Tasks (and microtasks which are more urgent, lower overhead tasks that can execute when the call stack is empty even while the main script is running) in a task queue wait for the stack to be empty before being executed. Page re-renders are also blocked by running code on the stack (long delays between re-renders are undesirable). Using callbacks and hence not blocking the task queue, allows re-rendering to be done more regularly, improving responsiveness. The precise behaviour of task de-queueing (and lower overhead microtasks) can actually differ between browsers, which causes considerable headache.
References:
Discussions over PRs, issues and generally attempting to solve issues, were a great way to explore design considerations. Here is a non-exhaustive list of interesting points that came up this semester:
In-house vs External Library
In implementing new functionality or extending existing functionality (Git interface for example), there is usually a question of whether it would be easier to maintain features in-house, or use external libraries. It might be a good idea to maintain core functionality in-house since we'd want more fine-grained control over these features and new features can be added/fixed quickly as needed. At the same time, external libraries save time and cost of learning about and solving possibly complex problems.
External libraries can however introduce vulnerabilities (several incidences of dependency sabotage with npm packages like color.js and node-ipc hit fairly close to home over the course of the semester). Hence, selection of libraries should be a well-deliberated process and should include considerations like active-ness of the project and diversity of maintainers.
Recency vs Ubiquity
In maintaining versions of dependencies, it is often important to weigh upgrading to a new version to get the newest features against possibly alienating users who don't already have that version. Neither is necessarily better than the other and will likely depend on the nature of the product. A new product for developers would probably have users who want new versions with the bleeding edge of features. On the other hand products that already have a large user base and aimed at less technical users might want to favour ubiquitous versions. Since RepoSense is aimed at users of all skill levels, including novice developers, we often default to the later approach.
In a similar vein, it might be important to make sure that new features don't break backward compatibility so that the end-user won't face significant hindrances with making upgrades. At the same time, the need to be backwards compatible can be a root of evil, introducing all manners of hacks and fixes. This highlights the importance of foresight in the early stages of development. Also, deciding when to stop backwards compatibility with a significant version bump can be a non-trivial decision. Doing so should come with thorough migration documentation (sparse documentation for Vue2 -> Vue3 migration caused a lot of developer grievances).
Isolated Testing
While it's fairly obvious that modularity with tests is important and that components should be tested in isolation with unchanging inputs, it is easy to let slip lapses in the form of hidden dependencies that prevent components from being isolated, or having inputs that are actually non-static. Some of these issues came up over the course of the semester but it struck me just how easy it was for them to slip by unnoticed. There aren't necessarily language-level features that enforce coupling rules for the most part since many of these dependencies can be quite implicit.
This had me thinking about the importance of being explicit in crucial sections of code, as described below.
Being Explicit
It is important that programmers make the behaviour of certain crucial sections of code as explicit as possible. One way of doing this is through good naming of methods and variables, and grouping statements semantically into methods or classes. Large chunks of code is detrimental and allows implicit slips in behaviour that can go unnoticed. So we might even want to make new special classes that do very specific things to make it clear that it is an important subroutine/behaviour that deserves its own abstraction.
At the same time, high reliance on object orientation can lead to too many classes, each class doing trivial things and with high coupling between the classes leading to spaghetti logic that doesn't do very much to alleviate implicit behaviour. There exists a delicate middle ground characterised by semantically well partitioned code.
Behavioural Consistency
The earlier section on Javascript quirks were a result of an overly accommodating feature integration during the early stages of development. It's become a cautionary tale of sorts of the importance of consistency and predictability in behaviour. In adding new features, it was personally very tempting to allow small inconsistencies in behaviour in favour of simplicity of implementation. While simplicity is a desirable outcome, I'd argue that consistency is more important (small inconsistencies can runaway into larger un-fixable differences).
Consistency can be with respect to various things. For example, we might want that identical inputs behave the same under similar conditions (differing in non-semantic respects) or that similar inputs (differing in non-semantic respects) behave the same under the identical conditions, etc.
git bisect
is a very nice way to find problematic commits. Given a bad commit and a previously good commit, git bisect
does a binary search (either automatically with a test or with manual intervention) to find the problematic commit where the issue was introduced.involves:USER
for all items that involve a user. This was very useful for updating progress.md
. More features here.Gradle is a very flexible build automation tool used for everything from testing and formatting, to builds and deployments. Unlike with other build automation tools like Maven where build scripts written in XML (a widely hated feature of the tool), Gradle build scripts are written in a domain specific language based on Groovy or Kotlin, which are both JVM based languages. This means that it can interact seamlessly with Java libraries.
Gradle is also much more performant than alternatives like Maven because of its:
RepoSense recently added functionality for hot reload on frontend as a Gradle task, which made frontend development a lot more productive. Unfortunately, the feature isn't available on Linux because the package we were using (Apache Ant's condition package) could not specifically check for it. Migrating to Gradle's own platform package recently taken out of incubation, allowed us to support all 3 prominent operating systems.
References:
Like Gradle, Github Actions help with automation of workflows like CI/CD and project management, and can be triggered by a variety of events (pull requests, issues, releases, forks, etc). It also has a growing library of plugins that make workflows a lot easier to set-up. I was surprised that there is some nice tooling support for GitHub actions on IntelliJ.
GitHub actions allows users to run CI on a variety of operating systems, such as Ubuntu XX.04, macOS and Windows Server (which is virtually the same as Windows 10/11 but with better hardware support and more stringent security features).
GitHub also provides a variety of API to interact with these objects. One quirk I came across with the API was that posting single comments on pull requests need to go through the issues endpoint instead of the pulls endpoint (the endpoint for pulls requires code to be referenced). This doesn't cause problems since issues and pulls will never have identical IDs.
GitHub deployment APIs also returns deployment information in pages, which is a sensible thing to do but can cause slight inconvenience when long running PRs have more deployments than can fit in a page.
Actions and APIs also have some great documentation:
Git exploded in popularity in large part due to Git hosting providers like GitHub. GitLab and Bitbucket are also commonly used Git hosts. RepoSense has thus far only largely supported GitHub, but there is a clear incentive to support other commonly used remotes. This is made a little challenging due to differences in conventions between the sites:
base_url | Commit View | Blame View | |
---|---|---|---|
GitHub | github.com | {base_url}/{org}/{repo_name}/commit/{commit_hash} | {base_url}/{org}/{repo_name}/blame/{branch}/{file_path} |
GitLab | gitlab.com | {base_url}/{org}/{repo_name}/-/commit/{commit_hash} | {base_url}/{org}/{repo_name}/-/blame/{branch}/{file_path} |
Bitbucket | bitbucket.org | {base_url}/{org}/{repo_name}/commits/{commit_hash} | {base_url}/{org}/{repo_name}/annotate/{branch}/{file_path} |
For example, Bitbucket uses the term 'annotate' instead of 'blame' because the word 'blame' is insufficiently positive.
In investigating the output of git remote -v
, I noticed there were 2 remotes (fetch and push) for each remote name, which I was confused by. The utility of the separation of fetch and push remotes is for triangular workflows.
We are probably familiar with the common workflow for updating a branch on a forked repo which involves first pulling updates from upstream master, then making changes and pushing to our own fork. This requires remembering to fetch and push to separate repos. With triangular workflows, you can have fetch and push apply to separate repos but with the same remote name, which makes this process much more convenient.
Cypress is a frontend testing tool for testing applications that run in the browser, with tests that are easy to read and write. It uses browser automation (similar to Selenium) and comes with a browser and relevant dependencies out of the box, so it's very easy to set-up. Cypress also provides a dashboard for convenient monitoring of test runs.
https://docs.cypress.io/guides/overview/why-cypress#In-a-nutshell
Bash scripts can be run in a github action workflow, which greatly expands the scope of things you can do with actions. Bash is quite expressive (I hadn't realised just how much it could do). some cool things I learned you could do:
$*
gives parameter values separated by the value in IFS
(Internal File Separator).python3
with a -c
flag and perform more complex processing with a single line python program.ls 1> out 2> err
)Being relatively new to frontend tools, I found Vue.js to be quite interesting. Vue allows code reusability and abstractions through components. While I didn’t work extensively on the frontend, what I learned from the bits that I did work on was quite cool:
Vue state: I found it interesting that you could have computed properties that are accessed the same way as properties, but can depend on other properties and can dynamically update when these properties change. This is often more elegant than using a Vue watcher to update a field. You can even have computed setters that update dependent properties when set. A watcher, however, can be more appropriate when responses to changing data are expensive or need to be done asynchronously.
Vue custom directives: Directives are ways to reuse lower level DOM logic. Directives can define vue life-cycle hooks and later be bound to components (can actually take in any JavaScript object literal). For implementing lazy loads, I needed to use the vue-observe-visibility (external library) directive with slight modification to the hooks to be compatible with Vue3.
References:
Pug is a templating language that compiles to HTML. It is less verbose and much more maintainable than HTML, and also allows basic presentation logic with conditionals, loops and case statements.
There are a lot of these, and most just remain quirks but some result in actual unintended bugs in production (often in edge cases). It was interesting to see this in our contribution bar logic. A technique sometimes used to extract the integer part of a number is to use parseInt
(it's even suggested in a Stack Overflow answer). It turns out we were using this for calculating the number of contribution bars to display for a user. This works for most values, but breaks when numbers become very large or small (less than 10^-7). In this unlikely situation, we'd display anywhere from 1 to 9 extra bars (moral: use Math.floor
instead!).
An investigation into string representations in browsers led me down a rabbit hole of JavaScript runtimes and engines, and ultimately improved my understanding of JavaScript in general. Different browsers have different JS engines - Chrome uses V8, Firefox uses SpiderMonkey (the original JS engine written by Brendan Eich), Edge used to use Chakra but now also uses V8, Safari uses WebKit, etc. Engines often differ significantly in terms of the pipeline for code execution, garbage collection, and more.
The V8 engine as an example, first parses JavaScript into an Abstract Syntax Tree (AST) which is then compiled into bytecode. This bytecode is interpreted by the Ignition interpreter (Ignition also handles compilation of the AST into bytecode). Code that is revisited often during interpretation is marked "hot" and compiled further into highly efficient machine code. This technique of optimising compilation based on run-time profiling (Just-In-Time (JIT) compilation) is also used in other browser engines like SpiderMonkey and the JVM.
The engine is used for running things that are on the browser stack. JS is run in a single thread, and asynchronous tasks are done through callbacks in a task queue. The main script is first run, with things like promises and timeouts inserting tasks into a task queue. Tasks (and microtasks which are more urgent, lower overhead tasks that can execute when the call stack is empty even while the main script is running) in a task queue wait for the stack to be empty before being executed. Page re-renders are also blocked by running code on the stack (long delays between re-renders are undesirable). Using callbacks and hence not blocking the task queue, allows re-rendering to be done more regularly, improving responsiveness. The precise behaviour of task de-queueing (and lower overhead microtasks) can actually differ between browsers, which causes considerable headache.
References:
Discussions over PRs, issues and generally attempting to solve issues, were a great way to explore design considerations. Here is a non-exhaustive list of interesting points that came up this semester:
In-house vs External Library
In implementing new functionality or extending existing functionality (Git interface for example), there is usually a question of whether it would be easier to maintain features in-house, or use external libraries. It might be a good idea to maintain core functionality in-house since we'd want more fine-grained control over these features and new features can be added/fixed quickly as needed. At the same time, external libraries save time and cost of learning about and solving possibly complex problems.
External libraries can however introduce vulnerabilities (several incidences of dependency sabotage with npm packages like color.js and node-ipc hit fairly close to home over the course of the semester). Hence, selection of libraries should be a well-deliberated process and should include considerations like active-ness of the project and diversity of maintainers.
Recency vs Ubiquity
In maintaining versions of dependencies, it is often important to weigh upgrading to a new version to get the newest features against possibly alienating users who don't already have that version. Neither is necessarily better than the other and will likely depend on the nature of the product. A new product for developers would probably have users who want new versions with the bleeding edge of features. On the other hand products that already have a large user base and aimed at less technical users might want to favour ubiquitous versions. Since RepoSense is aimed at users of all skill levels, including novice developers, we often default to the later approach.
In a similar vein, it might be important to make sure that new features don't break backward compatibility so that the end-user won't face significant hindrances with making upgrades. At the same time, the need to be backwards compatible can be a root of evil, introducing all manners of hacks and fixes. This highlights the importance of foresight in the early stages of development. Also, deciding when to stop backwards compatibility with a significant version bump can be a non-trivial decision. Doing so should come with thorough migration documentation (sparse documentation for Vue2 -> Vue3 migration caused a lot of developer grievances).
Isolated Testing
While it's fairly obvious that modularity with tests is important and that components should be tested in isolation with unchanging inputs, it is easy to let slip lapses in the form of hidden dependencies that prevent components from being isolated, or having inputs that are actually non-static. Some of these issues came up over the course of the semester but it struck me just how easy it was for them to slip by unnoticed. There aren't necessarily language-level features that enforce coupling rules for the most part since many of these dependencies can be quite implicit.
This had me thinking about the importance of being explicit in crucial sections of code, as described below.
Being Explicit
It is important that programmers make the behaviour of certain crucial sections of code as explicit as possible. One way of doing this is through good naming of methods and variables, and grouping statements semantically into methods or classes. Large chunks of code is detrimental and allows implicit slips in behaviour that can go unnoticed. So we might even want to make new special classes that do very specific things to make it clear that it is an important subroutine/behaviour that deserves its own abstraction.
At the same time, high reliance on object orientation can lead to too many classes, each class doing trivial things and with high coupling between the classes leading to spaghetti logic that doesn't do very much to alleviate implicit behaviour. There exists a delicate middle ground characterised by semantically well partitioned code.
Behavioural Consistency
The earlier section on Javascript quirks were a result of an overly accommodating feature integration during the early stages of development. It's become a cautionary tale of sorts of the importance of consistency and predictability in behaviour. In adding new features, it was personally very tempting to allow small inconsistencies in behaviour in favour of simplicity of implementation. While simplicity is a desirable outcome, I'd argue that consistency is more important (small inconsistencies can runaway into larger un-fixable differences).
Consistency can be with respect to various things. For example, we might want that identical inputs behave the same under similar conditions (differing in non-semantic respects) or that similar inputs (differing in non-semantic respects) behave the same under the identical conditions, etc.
git bisect
is a very nice way to find problematic commits. Given a bad commit and a previously good commit, git bisect
does a binary search (either automatically with a test or with manual intervention) to find the problematic commit where the issue was introduced.involves:USER
for all items that involve a user. This was very useful for updating progress.md
. More features here.Week | Achievements |
---|---|
1 | Submitted Issue: Tasks To Self-Test Knowledge Unhide Activity Dashboard #221 |
1 | Submitted Issue: Bug when not entering anything into Select repo dialog #224 |
1 | Submitted Issue: Show a list of hidden users at the end #225 |
1 | Submitted Issue: Error toast shown on selecting p.Low or priority.Low label on WATcher repository #227 |
2 | Submitted Issue: Move Activity Dashboard from prototype to release #232 |
4 | Commented on Issue: Bypass logging in if viewing public repos only #236 |
4 | Commented on Issue: Hiding labels do not work as expected #240 |
Week | Achievements |
---|---|
1 | Reviewed PR: Hide 0 issue columns #223 |
1-4 | Reviewed PR: Keep filters option when switching repos #226 |
2 | Reviewed PR: Prevent redirection when repo not set #228 |
2-4 | Reviewed PR: Fix label filter not working #230 |
4 | Reviewed PR: Improve activity dashboard design #233 |
4 | Commented on PR: Refactor test cases (In progress) #234 |
4 | Reviewed PR: Show list of hidden users #235 |
4 | Reviewed PR: Remove unused services #238 |
Week | Achievements |
---|---|
4 | Contributed PR: Build in Github Actions #239 |
Week | Achievements |
---|---|
1 | Commented on PR: Redirect invalid routes to 404 not found page #1238 |
Week | Achievements |
---|---|
1 | Submitted Issue: Tasks To Self-Test Knowledge Unhide Activity Dashboard #221 |
1 | Submitted Issue: Bug when not entering anything into Select repo dialog #224 |
1 | Submitted Issue: Show a list of hidden users at the end #225 |
1 | Submitted Issue: Error toast shown on selecting p.Low or priority.Low label on WATcher repository #227 |
2 | Submitted Issue: Move Activity Dashboard from prototype to release #232 |
4 | Commented on Issue: Bypass logging in if viewing public repos only #236 |
4 | Commented on Issue: Hiding labels do not work as expected #240 |
Week | Achievements |
---|---|
1 | Reviewed PR: Hide 0 issue columns #223 |
1-4 | Reviewed PR: Keep filters option when switching repos #226 |
2 | Reviewed PR: Prevent redirection when repo not set #228 |
2-4 | Reviewed PR: Fix label filter not working #230 |
4 | Reviewed PR: Improve activity dashboard design #233 |
4 | Commented on PR: Refactor test cases (In progress) #234 |
4 | Reviewed PR: Show list of hidden users #235 |
4 | Reviewed PR: Remove unused services #238 |
Week | Achievements |
---|---|
4 | Contributed PR: Build in Github Actions #239 |
Week | Achievements |
---|---|
1 | Commented on PR: Redirect invalid routes to 404 not found page #1238 |
One of the largest takeaways from working with MarkBind in the last semester has been Vue.js, an open-source front-end framework that MarkBind uses to build it's UI components. Previously, only knowing the React.js framework, Vue.js is a handy addition to my arsenal. The basics of Vue.js was rather simple to pick up. Reading the Vue.js documentation, and referencing examples of already implemented Vue components in MarkBind, I quickly understood the use of <template>
, <style>
and <script>
. Through Markbind's Developer Guide, I learnt how to easily create different kinds of Vue components and implement them in MarkBind.
As I implemented my first Vue component, Add autogenerated breadcrumbs component #2193, I delved deeper into Vue, exploring the use of data()
, to manage the internal state of Vue components, and methods()
to define methods to be used within the component. I also learnt more about Vue lifecycle hooks, in which I used the mounted
hook to allow the Breadcrumb component to query the SiteNav to figure out the hierarchy of the current page.
As I continued working on improving MarkBind's frontend, I learnt more about Vue's <transition>
component, in particular using transition hooks. While I was working on Fix Quiz expanding between questions #2184, I came realize how useful these hooks were, helping to create seamless transitions for different situations. I relied heavily on Vue.js documentation and StackOverflow Posts as I was researching about Vue's transition hooks.
When I was working on implementing the new Breadcrumb and Collapse/Expand All Buttons components, I had to extensively use Document.querySelector()
and other related methods. I was new to this and had to do some research about how the methods work, what happens if the object cannot be found and handling edge cases. By practicing these while implementing the two components mentioned above, I believe that I have become more proficient in doing this. As a side-effect of this, I have also gained a deeper understanding on how the DOM works.
Resources:
Jest and Vue Test Utils were something that I was new to coming into MarkBind. MarkBind uses Jest together with Vue Test Utils for its snapshot tests, which test Vue components against their expected snapshots. As I was updating and implementing Vue components, I had to update and create the relevant test suites to ensure that the Vue components that I was updating or creating were working as expected. I explored mounting the components, attaching components to a document to allow another component to interact with them.
Resources:
As MarkBind is undergoing a migration to Typescript, I put in some time to learn basic Typescript. This was important as mid-way through the semester, as many of the files were being migrated to Typescript. This has also helped me in reviewing PRs that deals with Typescript migration and PRs which affect the Typscript files in MarkBind.
Resources:
When updating the looks of old components and creating new ones, I had to do some research about what makes a website visually pleasing. My most interesting finds were about the use of golden ratios in design and choosing complementary colours with tools such as Canva's Colour Wheel. I also learnt the different meanings of different icons through exploration and discussions with Update Breadcrumb icons #2265 and Add CollapseExpandButtons component.
I also internalized how to create transitions and effects that fit with the theme of the project, for MarkBind, had a more minimal theme. This was done when updating designs of components in Tweak sitenav design #2204, Update Question/Quiz component design #2131.
As I progressed to start managing the project, I started reviewing and merging PRs. Initially as I reviewed smaller PRs, I had little problem understanding the code and understanding where it can be improved. However, as I reviewed more complex PRs, I began having difficulties understanding the changes quickly. I came across a method to understand code in a more simple manner, the Rubber Duck Debugging method. Using this helped me to try and understand the code line by line and handle more complex changes more managably, helping me to understanding them better.
One of the largest takeaways from working with MarkBind in the last semester has been Vue.js, an open-source front-end framework that MarkBind uses to build it's UI components. Previously, only knowing the React.js framework, Vue.js is a handy addition to my arsenal. The basics of Vue.js was rather simple to pick up. Reading the Vue.js documentation, and referencing examples of already implemented Vue components in MarkBind, I quickly understood the use of <template>
, <style>
and <script>
. Through Markbind's Developer Guide, I learnt how to easily create different kinds of Vue components and implement them in MarkBind.
As I implemented my first Vue component, Add autogenerated breadcrumbs component #2193, I delved deeper into Vue, exploring the use of data()
, to manage the internal state of Vue components, and methods()
to define methods to be used within the component. I also learnt more about Vue lifecycle hooks, in which I used the mounted
hook to allow the Breadcrumb component to query the SiteNav to figure out the hierarchy of the current page.
As I continued working on improving MarkBind's frontend, I learnt more about Vue's <transition>
component, in particular using transition hooks. While I was working on Fix Quiz expanding between questions #2184, I came realize how useful these hooks were, helping to create seamless transitions for different situations. I relied heavily on Vue.js documentation and StackOverflow Posts as I was researching about Vue's transition hooks.
When I was working on implementing the new Breadcrumb and Collapse/Expand All Buttons components, I had to extensively use Document.querySelector()
and other related methods. I was new to this and had to do some research about how the methods work, what happens if the object cannot be found and handling edge cases. By practicing these while implementing the two components mentioned above, I believe that I have become more proficient in doing this. As a side-effect of this, I have also gained a deeper understanding on how the DOM works.
Resources:
Jest and Vue Test Utils were something that I was new to coming into MarkBind. MarkBind uses Jest together with Vue Test Utils for its snapshot tests, which test Vue components against their expected snapshots. As I was updating and implementing Vue components, I had to update and create the relevant test suites to ensure that the Vue components that I was updating or creating were working as expected. I explored mounting the components, attaching components to a document to allow another component to interact with them.
Resources:
As MarkBind is undergoing a migration to Typescript, I put in some time to learn basic Typescript. This was important as mid-way through the semester, as many of the files were being migrated to Typescript. This has also helped me in reviewing PRs that deals with Typescript migration and PRs which affect the Typscript files in MarkBind.
Resources:
When updating the looks of old components and creating new ones, I had to do some research about what makes a website visually pleasing. My most interesting finds were about the use of golden ratios in design and choosing complementary colours with tools such as Canva's Colour Wheel. I also learnt the different meanings of different icons through exploration and discussions with Update Breadcrumb icons #2265 and Add CollapseExpandButtons component.
I also internalized how to create transitions and effects that fit with the theme of the project, for MarkBind, had a more minimal theme. This was done when updating designs of components in Tweak sitenav design #2204, Update Question/Quiz component design #2131.
As I progressed to start managing the project, I started reviewing and merging PRs. Initially as I reviewed smaller PRs, I had little problem understanding the code and understanding where it can be improved. However, as I reviewed more complex PRs, I began having difficulties understanding the changes quickly. I came across a method to understand code in a more simple manner, the Rubber Duck Debugging method. Using this helped me to try and understand the code line by line and handle more complex changes more managably, helping me to understanding them better.
Overall, I believe that because I was the least experienced (or at least I felt I was), I was also able to learn a whole lot from this module, especially front-end-wise.
While I used Angular to make a PR for TEAMMATES before the semester started, I think I still had a lot more to learn about it, like front-end unit testing (especially this because that initial PR had no tests at that point in time) which I was able to learn when I eventually actually made that PR in the real TEAMMATES repo. Due to the bindings, I had to pay especially close attention to the component testing scenarios of a component with inputs and outputs and a component inside a test host.
However, that was mostly component and snapshot testing. In order to also learn how to do testing for services, I also did testing for the feedback responses service. Though, I learned that testing services seemed largely similar to and yet much simpler than testing components.
Beyond testing, I also learned how to create services themselves in this onboarding task commit where I created the service to get feedback response statistics from the backend. I also learned how to integrate this service with the actual page component in order to actually obtain statistics to display using RxJS.
As for components or their templates, I learned about more about how to use Angular's HTML templates in order to direct inputs to and outputs from a component through property binding and event binding respectively. I also learned about how the custom structural directive tmIsLoading
worked in this PR as I was debugging when I initially wrongly caused the loading spinner to always display when I was in fact trying to display something else (eventually found out it was because I used the same boolean variable used to display the spinner, so don't be like me; check the usages of any variable you reuse). I also learned how to use <ng-container>
and <ng-template>
in that same PR, particularly with structural directives like ngIf
.
Resources:
In order to integrate Angular services that used asynchronous requests with components, I had to learn about Observables and Subscriptions from RxJS. I also had to learn other things from RxJS like the operators pipe
or first
for the previously mentioned component testing I did due to the fact that EventEmitter
objects used for event binding apparently functioned like RxJS Observable
objects.
Resources:
While I have taken some online web development courses in my free time before, I have actually never touched web development in a real project, only backend and mobile application development. Thus, doing some front-end work benefitted me a lot. For example, I was able to use my initially largely untested (and back then, slowly fading) knowledge of HTML and/or Bootstrap to some use such as in my onboarding task commits where I (re-)learned how to align everything nicely using the Bootstrap grid system (sorry if this is really basic) or in TEAMMATES PR #11628. Actually, after doing the front-end stuff in the onboarding task, I decided to go into the back-end for the deadline extensions feature so that I could learn TEAMMATES front to back, but perhaps I should have stayed in the front-end for the deadline extensions feature too to learn more. Still, virtually all my non-deadline extensions feature PRs were front-end related so maybe I was still able to learn as much as I could about the front-end.
Resources:
I learned how to use these to do front-end unit testing in Angular as previously mentioned, particularly things like expect
to check values are as expected, spyOn
to mock services, beforeEach
for common test setup code, and related attributes/functions (toBeTruthy()
, etc.).
Also, I learned about snapshot testing. I initially had no idea this existed before (sorry if this is basic), and yet it seems to be pretty widely used (?) so learning of its existence seemed important.
Resources:
I learned how to use D3 to display charts. I used this to create the feedback responses statistics chart.
Resources:
I was looking into the issue Instructor: Edit rubric question: reorder options using drag and drop #8933; I initially wanted to do a PR before my exams started but I unfortunately had no time to do so. Regardless, I was able to look into how I could possibly do it after my exams when I have time.
I looked through the code base to see how drag and drop is implemented in other question types such as in multiple choice questions and I found out that we use the CDK Drag and Drop module from Angular Material. Angular Material allows Material Design components to be added into Angular. From what I understand, Material Design provides a sort of library or system with customizable front-end components to provide pre-made UI functionality. I have actually used it previously when I did my own side projects for Android, though this is my first time using the drag and drop component (or similar) because it is currently not available on Android. Besides, I have also never used Material Design within Angular at all before.
The nice thing about Angular Material is it hides all the underlying code away and all that is minimally necessary to add is the cdkDrag
Angular directive. Unfortunately, from what I see, it seems that the drag and drop functionality provided by Angular Material does not work very well for table columns, which is the main focus of the issue. In general, it seems that tables are not well supported by Angular Material drag and drop, based on how tables are missing from the official documentation. Fortunately, there are workarounds like from this post from Stack Overflow and its linked StackBlitz project or from this blog post. However, these solutions do not produce satisfactory results, at least to me. When the columns are dragged along rows, the animations and "previews" do not show up for the rest of the rows, only for the row that was clicked on (such as the header). On the other hand, it does work well for dragging rows along columns. I suspect this has to do with how tables work in HTML, which is that they are essentially not really a single element but actually split into multiple table cell elements; this is unlike table rows which are single row elements. This means that Angular Material drag and drop probably works pretty well with rows, adding animations/previews. Unfortunately, this is not the case with columns. I believe that to enable this for table columns, it may be necessary after all to actually implement it from scratch after all, manually checking the location of the mouse and changing the columns appropriately to provide the animations/"previews" while dragging, or other similar implementations.
Still, this was interesting and I did learn things. I also believe that with this, adding drag and drop for the table rows would be pretty simple, if necessary. I could also look through how drag and drop is currrently done in Angular for inspiration on how to do it for the columns, or maybe it actually is possible to do it without implementing the functionality myself.
Resources:
I have previously used Firebase Cloud Firestore, an NoSQL database. I remember how when I used Firestore, I also noticed Datastore, but I just told myself to look at it at another time, and it seems like the time was now. Overall, I found out more about Datastore and how it works, like how it is also a NoSQL database, and I found similarities between entities and documents, and between kinds and collections, which was how I was able to understand it quickly.
For the deadline extensions feature, we had to maintain maps from email addresses to deadlines within the feedback session entities. I learned that this was not a standard Datastore value type so a possible way of storing this would be to store it as a Blob. I also learned that to do this within Objectify, this can be done through the Serialize annotation.
In order to validate requests to update the deadline maps, we needed to check if the emails in the requests actually existed for the corresponding course. One way would be to load every single CourseStudent
entity and every Instructor
entity. However, I learned that this costs a certain amount and not only that, the cost scales for every read of every instance. I found out about projection queries, which only scales with the number of queries, not the number of entities read in that query. This was more economical and thus, I chose to do this instead. Strangely, I do not think projection queries are documented in Objectify, so I had to refer to StackOverflow to find out how to do projection queries within Objectify.
I also learned that projection queries needed indices, and I initially wrongly thought that this was only for the properties that were projected, not other properties within the same query that were, say, filtered for instance. I also previously read that every property of each entity kind already has a built-in index of its own, so I initially wrongly assumed that I did not need to write any more indices for my projection queries. However, Fergus (I believe?) pointed out to me that this was wrong and looking at it again, it does make more sense for all properties used in a query, so both projections and filters, to require a composite index altogether. However, this then came with a downside, as I also found out that indices cost money to maintain too due to their storage costs.
Resources:
I have also only previously used Google Cloud Functions or Firebase Cloud Functions. I also remember how when I used either of them, I also noticed App Engine and then also told myself to look at it at another time, so getting to learn it by joining TEAMMATES, like Datastore, was such a great thing.
I think the main thing I learned was the task queues, though unfortunately, they are already phased out. I am at least hoping that this knowledge is transferable to what I believe is the new equivalent service of Google Cloud, which is Cloud Tasks. Regardless, I had to use task queues in order to run the workers created by Samuel which handle deadline extension entities for the deadline extensions feature.
Resources:
Overall, I believe that because I was the least experienced (or at least I felt I was), I was also able to learn a whole lot from this module, especially front-end-wise.
While I used Angular to make a PR for TEAMMATES before the semester started, I think I still had a lot more to learn about it, like front-end unit testing (especially this because that initial PR had no tests at that point in time) which I was able to learn when I eventually actually made that PR in the real TEAMMATES repo. Due to the bindings, I had to pay especially close attention to the component testing scenarios of a component with inputs and outputs and a component inside a test host.
However, that was mostly component and snapshot testing. In order to also learn how to do testing for services, I also did testing for the feedback responses service. Though, I learned that testing services seemed largely similar to and yet much simpler than testing components.
Beyond testing, I also learned how to create services themselves in this onboarding task commit where I created the service to get feedback response statistics from the backend. I also learned how to integrate this service with the actual page component in order to actually obtain statistics to display using RxJS.
As for components or their templates, I learned about more about how to use Angular's HTML templates in order to direct inputs to and outputs from a component through property binding and event binding respectively. I also learned about how the custom structural directive tmIsLoading
worked in this PR as I was debugging when I initially wrongly caused the loading spinner to always display when I was in fact trying to display something else (eventually found out it was because I used the same boolean variable used to display the spinner, so don't be like me; check the usages of any variable you reuse). I also learned how to use <ng-container>
and <ng-template>
in that same PR, particularly with structural directives like ngIf
.
Resources:
In order to integrate Angular services that used asynchronous requests with components, I had to learn about Observables and Subscriptions from RxJS. I also had to learn other things from RxJS like the operators pipe
or first
for the previously mentioned component testing I did due to the fact that EventEmitter
objects used for event binding apparently functioned like RxJS Observable
objects.
Resources:
While I have taken some online web development courses in my free time before, I have actually never touched web development in a real project, only backend and mobile application development. Thus, doing some front-end work benefitted me a lot. For example, I was able to use my initially largely untested (and back then, slowly fading) knowledge of HTML and/or Bootstrap to some use such as in my onboarding task commits where I (re-)learned how to align everything nicely using the Bootstrap grid system (sorry if this is really basic) or in TEAMMATES PR #11628. Actually, after doing the front-end stuff in the onboarding task, I decided to go into the back-end for the deadline extensions feature so that I could learn TEAMMATES front to back, but perhaps I should have stayed in the front-end for the deadline extensions feature too to learn more. Still, virtually all my non-deadline extensions feature PRs were front-end related so maybe I was still able to learn as much as I could about the front-end.
Resources:
I learned how to use these to do front-end unit testing in Angular as previously mentioned, particularly things like expect
to check values are as expected, spyOn
to mock services, beforeEach
for common test setup code, and related attributes/functions (toBeTruthy()
, etc.).
Also, I learned about snapshot testing. I initially had no idea this existed before (sorry if this is basic), and yet it seems to be pretty widely used (?) so learning of its existence seemed important.
Resources:
I learned how to use D3 to display charts. I used this to create the feedback responses statistics chart.
Resources:
I was looking into the issue Instructor: Edit rubric question: reorder options using drag and drop #8933; I initially wanted to do a PR before my exams started but I unfortunately had no time to do so. Regardless, I was able to look into how I could possibly do it after my exams when I have time.
I looked through the code base to see how drag and drop is implemented in other question types such as in multiple choice questions and I found out that we use the CDK Drag and Drop module from Angular Material. Angular Material allows Material Design components to be added into Angular. From what I understand, Material Design provides a sort of library or system with customizable front-end components to provide pre-made UI functionality. I have actually used it previously when I did my own side projects for Android, though this is my first time using the drag and drop component (or similar) because it is currently not available on Android. Besides, I have also never used Material Design within Angular at all before.
The nice thing about Angular Material is it hides all the underlying code away and all that is minimally necessary to add is the cdkDrag
Angular directive. Unfortunately, from what I see, it seems that the drag and drop functionality provided by Angular Material does not work very well for table columns, which is the main focus of the issue. In general, it seems that tables are not well supported by Angular Material drag and drop, based on how tables are missing from the official documentation. Fortunately, there are workarounds like from this post from Stack Overflow and its linked StackBlitz project or from this blog post. However, these solutions do not produce satisfactory results, at least to me. When the columns are dragged along rows, the animations and "previews" do not show up for the rest of the rows, only for the row that was clicked on (such as the header). On the other hand, it does work well for dragging rows along columns. I suspect this has to do with how tables work in HTML, which is that they are essentially not really a single element but actually split into multiple table cell elements; this is unlike table rows which are single row elements. This means that Angular Material drag and drop probably works pretty well with rows, adding animations/previews. Unfortunately, this is not the case with columns. I believe that to enable this for table columns, it may be necessary after all to actually implement it from scratch after all, manually checking the location of the mouse and changing the columns appropriately to provide the animations/"previews" while dragging, or other similar implementations.
Still, this was interesting and I did learn things. I also believe that with this, adding drag and drop for the table rows would be pretty simple, if necessary. I could also look through how drag and drop is currrently done in Angular for inspiration on how to do it for the columns, or maybe it actually is possible to do it without implementing the functionality myself.
Resources:
I have previously used Firebase Cloud Firestore, an NoSQL database. I remember how when I used Firestore, I also noticed Datastore, but I just told myself to look at it at another time, and it seems like the time was now. Overall, I found out more about Datastore and how it works, like how it is also a NoSQL database, and I found similarities between entities and documents, and between kinds and collections, which was how I was able to understand it quickly.
For the deadline extensions feature, we had to maintain maps from email addresses to deadlines within the feedback session entities. I learned that this was not a standard Datastore value type so a possible way of storing this would be to store it as a Blob. I also learned that to do this within Objectify, this can be done through the Serialize annotation.
In order to validate requests to update the deadline maps, we needed to check if the emails in the requests actually existed for the corresponding course. One way would be to load every single CourseStudent
entity and every Instructor
entity. However, I learned that this costs a certain amount and not only that, the cost scales for every read of every instance. I found out about projection queries, which only scales with the number of queries, not the number of entities read in that query. This was more economical and thus, I chose to do this instead. Strangely, I do not think projection queries are documented in Objectify, so I had to refer to StackOverflow to find out how to do projection queries within Objectify.
I also learned that projection queries needed indices, and I initially wrongly thought that this was only for the properties that were projected, not other properties within the same query that were, say, filtered for instance. I also previously read that every property of each entity kind already has a built-in index of its own, so I initially wrongly assumed that I did not need to write any more indices for my projection queries. However, Fergus (I believe?) pointed out to me that this was wrong and looking at it again, it does make more sense for all properties used in a query, so both projections and filters, to require a composite index altogether. However, this then came with a downside, as I also found out that indices cost money to maintain too due to their storage costs.
Resources:
I have also only previously used Google Cloud Functions or Firebase Cloud Functions. I also remember how when I used either of them, I also noticed App Engine and then also told myself to look at it at another time, so getting to learn it by joining TEAMMATES, like Datastore, was such a great thing.
I think the main thing I learned was the task queues, though unfortunately, they are already phased out. I am at least hoping that this knowledge is transferable to what I believe is the new equivalent service of Google Cloud, which is Cloud Tasks. Regardless, I had to use task queues in order to run the workers created by Samuel which handle deadline extension entities for the deadline extensions feature.
Resources:
Python is a high-level, general-purpose programming language. CPython is the reference implementation of the Python programming language. Written in C and Python, CPython is the default and most widely used implementation of the Python language.
I added a more meaningful error message when bytearray.extend
is incorrectly used with a str
object input, to tackle the bug highlighted in the GitHub issue, "bytearray.extend: Misleading error message".
str
is a built-in type in Python. str
objects are strings of text; strings are immutable sequences of Unicode code points. bytearray
is another built-in type in Python; bytearray
objects are mutable sequences of single bytes. bytearray.extend
can be used to add all the bytes of another sequence of bytes to the end of the bytearray
object. This means that bytearray.extend
can only be used with inputs that are sequences of individual bytes. In other words, str
objects cannot be used as input to bytearray.extend
because they are not sequences of single bytes.
When a str
object is passed as input into bytearray.extend
, Python correctly raises an error due to the type of the input. However, the error message is misleading, as it states TypeError: 'str' object cannot be interpreted as an integer
. The str
object mentioned can be interpreted as referring to the input passed, which seems to suggest that integers can be passed as input, which is incorrect because integers are not sequences, much less sequences of bytes. In reality, the str
object mentioned is referring to the elements of the sequence represented by the input str
object, which are themselves also str
objects.
The error message is not wrong. However, it is just misleading. The PR I contributed fixed this by doing a check when an error is raised for when the input is a str
object, before changing the error message to a more meaningful one, which would be TypeError: expected iterable of integers; got: 'str'
.
Python uses reStructuredText (RST) to document their project. RST is a lightweight markup language. It is not difficult to use, but it has its own syntax, which is different from the more popular markup languages like Markdown. I had to write a NEWS
entry[1] using RST. I used the Python Developer's Guide page on RST to help me figure out how to write using RST.
My first attempt at fixing the misleading error message was checking the type of the input very early on, even before any error was raised. I believe that in any other project, including in TEAMMATES, my first attempt might be seen as reasonable, and I think it might even be accepted, maybe after only a few minor changes, if any.
However, this was not the most performant way to fix the bug. Checking the type of the input before an error is raised means that the input would be checked even if the input was valid. The first review wanted me to change this, and so I did.
When I made my PR to fix the misleading error message, I was also required to write a NEWS
entry, just like almost every other PR made to the project. In the Python project, NEWS
entries document contributions so that it can be added into the changelog. They are necessary for any contribution made, except for those that do not affect users of the Python programming language itself, including:
NEWS
entryFrom what I understand, changes that are more significant can be highlighted in "What's New in Python" entries.
In comparison, I do not think this is done in TEAMMATES. I think all the changes are mentioned equally in the releases.
If somebody wants to fix a typo in the Python project, they do not need to post a new issue before making a pull request. They can simply make the pull request immediately. From what I know, this is not the case in TEAMMATES. At the very least, it is not explicitly mentioned in the TEAMMATES developer guide.
Instead of displaying all the changes equally, it may be better to highlight some of them, as they be more significant to more users. Users may not notice those changes if they are displayed equally with the rest, even it may be of interest to them.
For minor contributions, it seems like it would be overkill to need to post an issue before a pull request can be made. If it is not already the case, then maybe we should allow minor contributions without their own GitHub issues. We should also make it clear in the developer guide that this is allowed.
In the Python project, NEWS
entries document contributions so that it can be added into the changelog.
Python is a high-level, general-purpose programming language. CPython is the reference implementation of the Python programming language. Written in C and Python, CPython is the default and most widely used implementation of the Python language.
I added a more meaningful error message when bytearray.extend
is incorrectly used with a str
object input, to tackle the bug highlighted in the GitHub issue, "bytearray.extend: Misleading error message".
str
is a built-in type in Python. str
objects are strings of text; strings are immutable sequences of Unicode code points. bytearray
is another built-in type in Python; bytearray
objects are mutable sequences of single bytes. bytearray.extend
can be used to add all the bytes of another sequence of bytes to the end of the bytearray
object. This means that bytearray.extend
can only be used with inputs that are sequences of individual bytes. In other words, str
objects cannot be used as input to bytearray.extend
because they are not sequences of single bytes.
When a str
object is passed as input into bytearray.extend
, Python correctly raises an error due to the type of the input. However, the error message is misleading, as it states TypeError: 'str' object cannot be interpreted as an integer
. The str
object mentioned can be interpreted as referring to the input passed, which seems to suggest that integers can be passed as input, which is incorrect because integers are not sequences, much less sequences of bytes. In reality, the str
object mentioned is referring to the elements of the sequence represented by the input str
object, which are themselves also str
objects.
The error message is not wrong. However, it is just misleading. The PR I contributed fixed this by doing a check when an error is raised for when the input is a str
object, before changing the error message to a more meaningful one, which would be TypeError: expected iterable of integers; got: 'str'
.
Python uses reStructuredText (RST) to document their project. RST is a lightweight markup language. It is not difficult to use, but it has its own syntax, which is different from the more popular markup languages like Markdown. I had to write a NEWS
entry[1] using RST. I used the Python Developer's Guide page on RST to help me figure out how to write using RST.
My first attempt at fixing the misleading error message was checking the type of the input very early on, even before any error was raised. I believe that in any other project, including in TEAMMATES, my first attempt might be seen as reasonable, and I think it might even be accepted, maybe after only a few minor changes, if any.
However, this was not the most performant way to fix the bug. Checking the type of the input before an error is raised means that the input would be checked even if the input was valid. The first review wanted me to change this, and so I did.
When I made my PR to fix the misleading error message, I was also required to write a NEWS
entry, just like almost every other PR made to the project. In the Python project, NEWS
entries document contributions so that it can be added into the changelog. They are necessary for any contribution made, except for those that do not affect users of the Python programming language itself, including:
NEWS
entryFrom what I understand, changes that are more significant can be highlighted in "What's New in Python" entries.
In comparison, I do not think this is done in TEAMMATES. I think all the changes are mentioned equally in the releases.
If somebody wants to fix a typo in the Python project, they do not need to post a new issue before making a pull request. They can simply make the pull request immediately. From what I know, this is not the case in TEAMMATES. At the very least, it is not explicitly mentioned in the TEAMMATES developer guide.
Instead of displaying all the changes equally, it may be better to highlight some of them, as they be more significant to more users. Users may not notice those changes if they are displayed equally with the rest, even it may be of interest to them.
For minor contributions, it seems like it would be overkill to need to post an issue before a pull request can be made. If it is not already the case, then maybe we should allow minor contributions without their own GitHub issues. We should also make it clear in the developer guide that this is allowed.
In the Python project, NEWS
entries document contributions so that it can be added into the changelog.
Week | Achievements |
---|---|
2 - 3 | Authored PR (merged): [#12048] Migrate GetOngoingSessionsAction for V9 |
2 - 3 | Reviewed PR: [#12048] Migrate RemoveDataBundle |
2 - 6 | Mentored Marques for onboarding task: [#12048] Migrate UpdateStudentAction |
3 - 4 | Reviewed PR: [#12048] Refactor email generator |
4 | Reviewed PR: [#12048] Migrate SubmitFeedbackResponseAction's Logic and Db methods |
5 - 6 | Reviewed PR: [#12048] Migrate UpdateStudentAction |
5 | Reviewed PR: [#12048] Migrate FeedbackSessionClosedRemindersAction |
5 - 6 | Reviewed PR: [#12048] Migrate FeedbackSessionOpeningRemindersAction |
6 - Rc | Reviewed PR: [#12048] SQL injection test for AccountRequestsDbIT |
Rc | Reviewed PR: [#12048] SQL injection test for FeedbackQuestionsDbIT |
Rc | Reviewed PR: [#12048] SQL injection test for CoursesDbIT |
Rc | Reviewed PR: [#12048] SQL Injection Test for FeedbackResponsesDb |
Rc | Reviewed PR: [#12048] SQL injection test for AccountsDbIT |
Rc | Reviewed PR: [#12048] SQL injection test for UsersDbIT |
Week | Achievements |
---|---|
2 - 3 | Authored PR (merged): [#12048] Migrate GetOngoingSessionsAction for V9 |
2 - 3 | Reviewed PR: [#12048] Migrate RemoveDataBundle |
2 - 6 | Mentored Marques for onboarding task: [#12048] Migrate UpdateStudentAction |
3 - 4 | Reviewed PR: [#12048] Refactor email generator |
4 | Reviewed PR: [#12048] Migrate SubmitFeedbackResponseAction's Logic and Db methods |
5 - 6 | Reviewed PR: [#12048] Migrate UpdateStudentAction |
5 | Reviewed PR: [#12048] Migrate FeedbackSessionClosedRemindersAction |
5 - 6 | Reviewed PR: [#12048] Migrate FeedbackSessionOpeningRemindersAction |
6 - Rc | Reviewed PR: [#12048] SQL injection test for AccountRequestsDbIT |
Rc | Reviewed PR: [#12048] SQL injection test for FeedbackQuestionsDbIT |
Rc | Reviewed PR: [#12048] SQL injection test for CoursesDbIT |
Rc | Reviewed PR: [#12048] SQL Injection Test for FeedbackResponsesDb |
Rc | Reviewed PR: [#12048] SQL injection test for AccountsDbIT |
Rc | Reviewed PR: [#12048] SQL injection test for UsersDbIT |
Vue is a frontend JavaScript framework to build web user interfaces, while Pug is a preprocessor that speeds up writing HTML. In RepoSense, Vue brings the interactivity in the generated reports and Pug makes it easier to write more concise and readable HTML.
Having some experience in frontend libraries/frameworks like React, I did not have too steep a learning curve when learning Vue; however, I still took some time to get used to concepts and the syntax of Vue including state management, the idea of single-file components, conditional rendering, reactive UI elements, and much more. One particular aspect I enjoyed learning and implementing in Vue was the ease of declaring state in a component just within the data()
function. This was, to me, contrasted with React where useState
and useEffect
are more complicated and tricky to use.
Cypress is a testing framework that allows for end-to-end testing of web applications. I used it in RepoSense to write tests for the UI.
Cypress was a new tool to me and I had to learn how to write tests using this tool as well as how to set up the test environment. Many Cypress commands are based on natural words like .then
, .get
, .to.deep
, just to name a few, but the concepts of Cypress like asynchonicity, closures, and its inclusion of jQuery make it unfamiliar to me.
...
Vue is a frontend JavaScript framework to build web user interfaces, while Pug is a preprocessor that speeds up writing HTML. In RepoSense, Vue brings the interactivity in the generated reports and Pug makes it easier to write more concise and readable HTML.
Having some experience in frontend libraries/frameworks like React, I did not have too steep a learning curve when learning Vue; however, I still took some time to get used to concepts and the syntax of Vue including state management, the idea of single-file components, conditional rendering, reactive UI elements, and much more. One particular aspect I enjoyed learning and implementing in Vue was the ease of declaring state in a component just within the data()
function. This was, to me, contrasted with React where useState
and useEffect
are more complicated and tricky to use.
Cypress is a testing framework that allows for end-to-end testing of web applications. I used it in RepoSense to write tests for the UI.
Cypress was a new tool to me and I had to learn how to write tests using this tool as well as how to set up the test environment. Many Cypress commands are based on natural words like .then
, .get
, .to.deep
, just to name a few, but the concepts of Cypress like asynchonicity, closures, and its inclusion of jQuery make it unfamiliar to me.
...
Week | Achievements |
---|---|
1 | Reviewed PR: Standardise Array Style for TS Files #2084 |
1 | Authored PR: Remove hash symbol from URL when decoding hash #2086 |
1 | Authored PR: Remove redundant Segment class #2085 |
2 | Reviewed PR: Updating SystemTestUtil::assertJson to compare Json objects instead of line-by-line analysis #2087 |
2 | Authored PR: Remove redundant User class #2093 |
2 | Reviewed PR: Update .stylelintrc.json to check for spacing #2094 |
3 | Reviewed PR: Extract c-authorship-file component from views/c-authorship #2096 |
3 | Reviewed PR: Use syntax coloring for code blocks in docs #2099 |
5 | Authored PR: Fix failing zoomFeature cypress test #2114 |
5 | Authored PR: Add search by tag functionality #2116 |
5 | Reviewed PR: Move Segment CSS into segment.vue #2113 |
6 | Created Issue: TypeError when visiting a certain URL #2123 |
6 | Authored PR: Fix zoom bug if zUser is undefined #2126 |
6 | Created Issue: Add a way to highlight and scroll a particular repo/author #2130 |
Week | Achievements |
---|---|
1 | Reviewed PR: Standardise Array Style for TS Files #2084 |
1 | Authored PR: Remove hash symbol from URL when decoding hash #2086 |
1 | Authored PR: Remove redundant Segment class #2085 |
2 | Reviewed PR: Updating SystemTestUtil::assertJson to compare Json objects instead of line-by-line analysis #2087 |
2 | Authored PR: Remove redundant User class #2093 |
2 | Reviewed PR: Update .stylelintrc.json to check for spacing #2094 |
3 | Reviewed PR: Extract c-authorship-file component from views/c-authorship #2096 |
3 | Reviewed PR: Use syntax coloring for code blocks in docs #2099 |
5 | Authored PR: Fix failing zoomFeature cypress test #2114 |
5 | Authored PR: Add search by tag functionality #2116 |
5 | Reviewed PR: Move Segment CSS into segment.vue #2113 |
6 | Created Issue: TypeError when visiting a certain URL #2123 |
6 | Authored PR: Fix zoom bug if zUser is undefined #2126 |
6 | Created Issue: Add a way to highlight and scroll a particular repo/author #2130 |
Week | Achievements |
---|---|
1 | Reviewed PR: Add whitespace validation #1237 |
2 | Reviewed PR: Fix broken duplicate link #1233 |
2 | Reviewed PR: Redirect invalid routes to 404 not found page #1238 |
4 | Reviewed PR: Preserve line breaks in markdown #1241 |
Week | Achievements |
---|---|
3 | Contributed PR: Add documentation for CATcher's parser #1240 |
Week | Achievements |
---|---|
4 | Reviewed PR: Refactor test cases for Login Component, Session Model and Conflict Model #241 |
4 | Reviewed PR: Remove markdown.css from test env stylesheets #243 |
4 | Reviewed PR: Refactor test cases for issue paginator #244 |
4 | Reviewed PR: Refactor test cases for issue sorter #245 |
4 | Reviewed PR: Refactor github label constants #246 |
4 | Reviewed PR: Refactor test cases for search filter #247 |
Week | Achievements |
---|---|
5 | Contributed PR: Simplify fix for abrupt panel transition #2421 |
Week | Achievements |
---|---|
1 | Reviewed PR: Add whitespace validation #1237 |
2 | Reviewed PR: Fix broken duplicate link #1233 |
2 | Reviewed PR: Redirect invalid routes to 404 not found page #1238 |
4 | Reviewed PR: Preserve line breaks in markdown #1241 |
Week | Achievements |
---|---|
3 | Contributed PR: Add documentation for CATcher's parser #1240 |
Week | Achievements |
---|---|
4 | Reviewed PR: Refactor test cases for Login Component, Session Model and Conflict Model #241 |
4 | Reviewed PR: Remove markdown.css from test env stylesheets #243 |
4 | Reviewed PR: Refactor test cases for issue paginator #244 |
4 | Reviewed PR: Refactor test cases for issue sorter #245 |
4 | Reviewed PR: Refactor github label constants #246 |
4 | Reviewed PR: Refactor test cases for search filter #247 |
Week | Achievements |
---|---|
5 | Contributed PR: Simplify fix for abrupt panel transition #2421 |
CreateInstructorAction
and InstructorSearchIndexingWorkerAction
FeedbackResponseCommentDbTest
Week | Achievements |
---|---|
3 | Merged PR: [#12048] Migrate Create Instructor action #12706 |
4 | Merged PR: [#12048] Migrate Instructor Search Indexing Worker action #12731 |
5 | Merged PR: [#12048] Add tests for Feedback Response Comment database #12755 |
CreateInstructorAction
and InstructorSearchIndexingWorkerAction
FeedbackResponseCommentDbTest
Week | Achievements |
---|---|
3 | Merged PR: [#12048] Migrate Create Instructor action #12706 |
4 | Merged PR: [#12048] Migrate Instructor Search Indexing Worker action #12731 |
5 | Merged PR: [#12048] Add tests for Feedback Response Comment database #12755 |
Of course, Angular is the framework used to run CATcher and WATcher, so learning how it works is an essential part of contributing to the projects. These projects are my first experience using Angular.
As I have experienced React.js and Alpine.js, with experience of working in frontend development during my internship, I expected to pick up Angular with ease. However, slightly different from my expectation, the OOP aspect of Angular makes it quite difficult to pick up.
There are a few interesting concepts that I picked up along the way:
@Component
to mark it as an Angular component. This decorator determines a few important properties of the component, including the query selector, the HTML template and the stylesheets.The knowledge of how a component is declared allows me to confidently create a new component in WATcher-PR#235, which was the component to show a list of users with 0 PRs and issues.
One interesting thing about Angular is that it provides a few methods that developers can make use of, to reduce the complexity of component class. This knowledge allows me to make WATcher-PR#230, where I directly modified the Angular model used in the HTML template.
I initially had a lot of trouble trying to understand the operators in RxJS. Ultimately, I was able to understand how it works, and the differences between different operators on an Observable
. I was able to see the similarities between different RxJS operators and Java stream
methods.
Observable::pipe
allows methods to modify the value within the Observable
, notably with map
and mergeMap
.Observable::subscribe
listens for changes within the Observable
.The knowledge of RxJS operators allow me to modify the underlying processes of the Angular services, and created CATcher-PR#1234, where I set branch for image uploads to main
.
Of course, Angular is the framework used to run CATcher and WATcher, so learning how it works is an essential part of contributing to the projects. These projects are my first experience using Angular.
As I have experienced React.js and Alpine.js, with experience of working in frontend development during my internship, I expected to pick up Angular with ease. However, slightly different from my expectation, the OOP aspect of Angular makes it quite difficult to pick up.
There are a few interesting concepts that I picked up along the way:
@Component
to mark it as an Angular component. This decorator determines a few important properties of the component, including the query selector, the HTML template and the stylesheets.The knowledge of how a component is declared allows me to confidently create a new component in WATcher-PR#235, which was the component to show a list of users with 0 PRs and issues.
One interesting thing about Angular is that it provides a few methods that developers can make use of, to reduce the complexity of component class. This knowledge allows me to make WATcher-PR#230, where I directly modified the Angular model used in the HTML template.
I initially had a lot of trouble trying to understand the operators in RxJS. Ultimately, I was able to understand how it works, and the differences between different operators on an Observable
. I was able to see the similarities between different RxJS operators and Java stream
methods.
Observable::pipe
allows methods to modify the value within the Observable
, notably with map
and mergeMap
.Observable::subscribe
listens for changes within the Observable
.The knowledge of RxJS operators allow me to modify the underlying processes of the Angular services, and created CATcher-PR#1234, where I set branch for image uploads to main
.
(To be added later)
PRs opened
Week | PR |
---|---|
<1 | #1233 Fix broken duplicate links |
<1 | #1234 Default branch to main |
4 | #1241 Preserve line breaks in markdown |
6 | #1245 Fix markddown blockquote preview difference |
PRs reviewed
Week | PR |
---|---|
5 | #1243 Faulty list view when back navigating |
PRs opened
Week | PR |
---|---|
2 | #230 Fix label filter not working |
3 | #235 Show list of hidden users |
4 | #254 Refactor Label model |
5 | #255 Add shareable repo-specific URL |
Issues created
Week | Issue |
---|---|
2 | #229 App filters do not work with some label names |
3 | #236 Bypass logging in if viewing public repos only |
3 | #240 Hiding labels do not work as expected |
4 | #251 Add shareable repo-specific URL |
PRs reviewed
Week | PR |
---|---|
6 | #261 Refactor sorting |
(To be added later)
PRs opened
Week | PR |
---|---|
<1 | #1233 Fix broken duplicate links |
<1 | #1234 Default branch to main |
4 | #1241 Preserve line breaks in markdown |
6 | #1245 Fix markddown blockquote preview difference |
PRs reviewed
Week | PR |
---|---|
5 | #1243 Faulty list view when back navigating |
PRs opened
Week | PR |
---|---|
2 | #230 Fix label filter not working |
3 | #235 Show list of hidden users |
4 | #254 Refactor Label model |
5 | #255 Add shareable repo-specific URL |
Issues created
Week | Issue |
---|---|
2 | #229 App filters do not work with some label names |
3 | #236 Bypass logging in if viewing public repos only |
3 | #240 Hiding labels do not work as expected |
4 | #251 Add shareable repo-specific URL |
PRs reviewed
Week | PR |
---|---|
6 | #261 Refactor sorting |
Pug, formerly known as Jade, is a templating language for Node.js and browsers. It simplifies HTML markup by using indentation-based syntax and offers features like variables, includes, mixins, and conditionals, making web development more efficient and readable.
I learnt how to create a Pug template and integrate it into a Vue component.
StackOverflow, ChatGPT, existing codebase, Pug Website
Vue.js is a progressive JavaScript framework used for building user interfaces and single-page applications. It offers a flexible and approachable structure for front-end development, with features like data binding, component-based architecture, and a simple yet powerful syntax.
I learnt the rationale behind the Single File Component structure, as well as how to implement it to refactor code. It was very similar to React in that the framework is structured around components, and props are still used for data flow. I also learnt how to access local files from within the framework to dynamically load data.
StackOverflow, ChatGPT, existing codebase, Vue Website
Cypress is an end-to-end testing framework used primarily for testing web applications. It provides a comprehensive set of tools and features to automate testing workflows, including real-time testing, automatic waiting, and built-in support for modern JavaScript frameworks.
I learnt how to write simple tests with the framework, as well as how to use the E2E live view to debug and design tests.
StackOverflow, ChatGPT, existing codebase, Cypress Documentation
Markdown-it is a popular JavaScript library used for parsing Markdown syntax and converting it into HTML. It provides a simple and flexible way to format text with lightweight markup syntax.
I learnt how to integrate markdown-it into a Vue component to allow for dynamic parsing of Markdown code into HTML for display.
StackOverflow, ChatGPT, markdown-it Documentation, this guide
Pug, formerly known as Jade, is a templating language for Node.js and browsers. It simplifies HTML markup by using indentation-based syntax and offers features like variables, includes, mixins, and conditionals, making web development more efficient and readable.
I learnt how to create a Pug template and integrate it into a Vue component.
StackOverflow, ChatGPT, existing codebase, Pug Website
Vue.js is a progressive JavaScript framework used for building user interfaces and single-page applications. It offers a flexible and approachable structure for front-end development, with features like data binding, component-based architecture, and a simple yet powerful syntax.
I learnt the rationale behind the Single File Component structure, as well as how to implement it to refactor code. It was very similar to React in that the framework is structured around components, and props are still used for data flow. I also learnt how to access local files from within the framework to dynamically load data.
StackOverflow, ChatGPT, existing codebase, Vue Website
Cypress is an end-to-end testing framework used primarily for testing web applications. It provides a comprehensive set of tools and features to automate testing workflows, including real-time testing, automatic waiting, and built-in support for modern JavaScript frameworks.
I learnt how to write simple tests with the framework, as well as how to use the E2E live view to debug and design tests.
StackOverflow, ChatGPT, existing codebase, Cypress Documentation
Markdown-it is a popular JavaScript library used for parsing Markdown syntax and converting it into HTML. It provides a simple and flexible way to format text with lightweight markup syntax.
I learnt how to integrate markdown-it into a Vue component to allow for dynamic parsing of Markdown code into HTML for display.
StackOverflow, ChatGPT, markdown-it Documentation, this guide
Week | Merged PRs |
---|---|
3 | [#1980] Standardise Array Style for Frontend Files #2084 |
3 | [#2003] Suppress Console Warning #2088 |
3 | [#1224] Update .stylelintrc.json to check for spacing #2094 |
4 | [#2001] Extract c-authorship-file component from views/c-authorship #2096 |
6 | [#2112] Move Segment CSS into segment.vue #2113 |
6 | [#467] Add Title Component #2102 |
7 | [#2128] Fix Blurry Favicon #2129 |
Week | PRs Reviewed |
---|---|
2 | [#2004] Remove redundant Segment class #2085 |
2 | [#1973] Remove redundant User class #2093 |
2 | [#2082] Fix typo in command in Setting Up page #2083 |
2 | [#2091] Improve memory usage by refactoring Regex compilation #2092 |
3 | [#2016] Remove hash symbol from URL when decoding hash #2086 |
3 | [#2103] Refactor parser package for greater organisation of classes #2104 |
3 | [#2098] Add show more button for error messages #2105 |
3 | [#1933] Fix broken DevOps Guide link in Learning Basics #2107 |
4 | [#1878] Updating SystemTestUtil::assertJson to compare Json objects instead of line-by-line analysis #2087 |
5 | [#2091] Minor Enhancements to Existing Regex Code #2115 |
5 | [#2117] Refactor CliArguments to conform to RepoConfiguration's Builder Pattern #2118 |
5 | [#2091] Minor Enhancements to Existing Regex Code #2115 |
6 | [#2109] Add search by tag functionality #2116 |
6 | [#2123] Fix zoom bug if zUser is undefined #2126 |
Week | Issues Submitted |
---|---|
2 | Update Style Checker for Pug Templates and Files #2097 |
5 | Support author-config.csv advanced syntax on CLI #2110 |
5 | Move CSS for Segment component into c-segment.vue #2112 |
7 | Fix Vulnerabilities in Code Base #2142 |
Week | Merged PRs |
---|---|
3 | [#1980] Standardise Array Style for Frontend Files #2084 |
3 | [#2003] Suppress Console Warning #2088 |
3 | [#1224] Update .stylelintrc.json to check for spacing #2094 |
4 | [#2001] Extract c-authorship-file component from views/c-authorship #2096 |
6 | [#2112] Move Segment CSS into segment.vue #2113 |
6 | [#467] Add Title Component #2102 |
7 | [#2128] Fix Blurry Favicon #2129 |
Week | PRs Reviewed |
---|---|
2 | [#2004] Remove redundant Segment class #2085 |
2 | [#1973] Remove redundant User class #2093 |
2 | [#2082] Fix typo in command in Setting Up page #2083 |
2 | [#2091] Improve memory usage by refactoring Regex compilation #2092 |
3 | [#2016] Remove hash symbol from URL when decoding hash #2086 |
3 | [#2103] Refactor parser package for greater organisation of classes #2104 |
3 | [#2098] Add show more button for error messages #2105 |
3 | [#1933] Fix broken DevOps Guide link in Learning Basics #2107 |
4 | [#1878] Updating SystemTestUtil::assertJson to compare Json objects instead of line-by-line analysis #2087 |
5 | [#2091] Minor Enhancements to Existing Regex Code #2115 |
5 | [#2117] Refactor CliArguments to conform to RepoConfiguration's Builder Pattern #2118 |
5 | [#2091] Minor Enhancements to Existing Regex Code #2115 |
6 | [#2109] Add search by tag functionality #2116 |
6 | [#2123] Fix zoom bug if zUser is undefined #2126 |
Week | Issues Submitted |
---|---|
2 | Update Style Checker for Pug Templates and Files #2097 |
5 | Support author-config.csv advanced syntax on CLI #2110 |
5 | Move CSS for Segment component into c-segment.vue #2112 |
7 | Fix Vulnerabilities in Code Base #2142 |
Having had experience in mainly React and NodeJS projects earlier, I was overall more used to creating projects with Functional Components, rather than Class Components as with Angular. However, I realised that one of the key aspects of frontend frameworks, namely reactivity, was in fact the main drivers of development of such frameworks in the first place!
In fact, even React were originally championing the idea of Class Components in order to isolate various web components into areas or responsibility, following rule number 1 of Software Engineering: Single Responsibility. However, while React is largely unopinionated in how you structure your code with regards to the coupling of business logic and HTML, Angular differs by dictating where and how you structure your components.
Angular separates components into modules which comprise of 3 to 4 files:
@Component
decorator;On the other hand, React only dictates that class components should produce some sort of HTML using the render function. Even this is removed with the introduction of Functional Components that are simply functions which render and produce some HTML. React introduces hooks which are often used by developers to manage some state at the component level, using functions with side effects.
Each method has its positives and negatives. Because of its opinionated nature, Angular makes it easy to standardize frontend coding standards and pattern across an entire enterprise, making it an apt choice to use as a tool for OSS development. On the other hand, React allows you to develop code more quickly, with more attention needed to be paid at the rendering lifecycles in order to let the Virtual DOM know when a particular component needs to be rendered again. On top of this, Angular wholely separates business logic from rendered HTML, whereas React takes the does not make this distinction.
Another key point is how React and Angular differentiate in providing context (sharing or passing down state between different branches of the DOM tree). React has its own Context API that is used to share some sort of state between different components, whereas Angular does this by the providers
declaration in the module folder, which results in a set of singletons that are shared by components that exist below it in the tree.
I also picked up RxJS along the way, which was Angular's answer to creating reactive components. RxJS essentially deals with asynchronous pipe/filter, publisher/subscriber behavior which allows values to change and other components or functions to subscribe to these changes. This works considering Angular's Change Detection strategy which I will explain later.
In comparison, React introduced and adopted hooks to encapsulate the behavior of having to rerender. React does this by operating on a Virtual DOM, and appropriately rerendering components and their children in patches when a change was detected. On the other hand, Angular does not have any abstraction to operate and rerender components whose state have changed. Instead, Angular uses a Change Detection Strategy which can be configured by the user (either onPush or Default). Angular Change Detection works by using Zone.js and activating after every async action performed. CD traversal starts at the root component (usually App) and works its way down the component tree updating the DOM as needed. What's happening under the hood is that browser events are registered into Zone.js - Angular's mechanism for orchestrating async events - which emits changes after initial template bindings are created. -...
StudentSearchIndexingWorkersAction
, GetSessionResultsAction
Week | Achievements |
---|---|
5 | Authored PR: Migrate StudentSearchIndexingWorkersAction #12733 |
R | Authored PR: Migrate GetSessionResultsAction #12719 |
StudentSearchIndexingWorkersAction
, GetSessionResultsAction
Week | Achievements |
---|---|
5 | Authored PR: Migrate StudentSearchIndexingWorkersAction #12733 |
R | Authored PR: Migrate GetSessionResultsAction #12719 |
Refine is a React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. It uses TypeScript. It simplifies the development process and eliminate repetitive tasks by providing industry-standard solutions for crucial aspects of a project, including authentication, access control, routing, networking, state management, and i18n.
Give a description of your contributions, including links to relevant PRs
PR 1: docs(core): add DataProvider interface definition #5653
Initially I thought contributing to documentation is easy, but I realize that contributing to documentation requires good understanding of the codebase structure and the workflow.
Give tools/technologies you learned here. Include resources you used, and a brief summary of the resource.
The PR review process in Refine is surprisingly fast. My PR is reviewed within one week.
Observations of contributing process:
Issues that are labelled good-first-issues often have comments that ask to be assigned the tasks. But the maintainers tend to take long to reply them. At the time they got back to potential contributers, contributers might already not be interested in it.
Refine has a Changeset
system where contributors need to label the impact on packages such as whether it requires a major version bump in any packages, as Refine uses a monorepo structure.
Refine is well-documented and its core team is quite active in issues, which is a hugh advantage for first time contributers because their questions got answered immediately. However, their good-first-issues still have high barriers to entry because of the complicated code structure. That being said, their maintainers make a good effort to explain what might need to be done to submit a PR in the issues, making it easier to understand.
Refine is a React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. It uses TypeScript. It simplifies the development process and eliminate repetitive tasks by providing industry-standard solutions for crucial aspects of a project, including authentication, access control, routing, networking, state management, and i18n.
Give a description of your contributions, including links to relevant PRs
PR 1: docs(core): add DataProvider interface definition #5653
Initially I thought contributing to documentation is easy, but I realize that contributing to documentation requires good understanding of the codebase structure and the workflow.
Give tools/technologies you learned here. Include resources you used, and a brief summary of the resource.
The PR review process in Refine is surprisingly fast. My PR is reviewed within one week.
Observations of contributing process:
Issues that are labelled good-first-issues often have comments that ask to be assigned the tasks. But the maintainers tend to take long to reply them. At the time they got back to potential contributers, contributers might already not be interested in it.
Refine has a Changeset
system where contributors need to label the impact on packages such as whether it requires a major version bump in any packages, as Refine uses a monorepo structure.
Refine is well-documented and its core team is quite active in issues, which is a hugh advantage for first time contributers because their questions got answered immediately. However, their good-first-issues still have high barriers to entry because of the complicated code structure. That being said, their maintainers make a good effort to explain what might need to be done to submit a PR in the issues, making it easier to understand.