Overview
Flashy is an application to help users organise their flashcards to help them memorise better.
Summary of contributions
-
Major enhancement: Implemented the bidirectional relationship between flashcards and tags.
-
What it does: Models the relationship between flashcards and tags: tags must have at least one flashcard, and flashcards can have any number of tags.
-
Justification: This feature serves as the main organizing system for flashcards in Flashy. It allows users to filter only flashcards they want to see.
-
-
Minor enhancement: updated the list command to be able to clear all filters for flashcards and tags, as well as show only cards without tags.
-
Code contributed: [Functional Code] [Test Code]
-
Other contributions:
-
Project enhancements:
-
Project management:
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Updated the Command Format Section
Justification: Users may be better able to interpret the command format section with an example.
Start of Extract [from: User Guide]
Reading the Command Format
This section will teach you how to write your own commands to interface with our application.
Let us walk you through the notation with the addc
command:
addc f/FRONT b/BACK [t/TAG]…
-
Words in
UPPER_CASE
are parameters you supply. These are often user-generated content.
In |
-
Items in square brackets are optional. In this example, tags are
|
-
Items with
…
after them can be used multiple times including zero times. In this example, multiple tags can be added to the card.
|
-
Parameters can be specified in any order. In this example,
addc f/front b/back
andaddc b/back f/front
are equivalent.
To access this help guide at any time, type help
into the command box and press Enter.
End of Extract
Update the list
command
Justification: the list command
Start of Extract [from: User Guide]
Clearing filters: list
The usage of list
is 2-fold:
-
list
allows you to see all your flashcards and tags by clearing all filters. -
list
can also be invoked with an additional flag aslist -t
, to list all cards without tags. This allows you to reorganize otherwise cards that are otherwise inaccessible.
Format: list [-t]
Examples:
-
list
Shows all tags and cards. -
list -t
Shows all tags, but shows only cards without tags.
End of Extract ---
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Start of Extract [from: Developer Guide]
Card-Tag Association
This section describes the implementation details on how we model the association between flashcards and tags.
The modeling of relations between flashcards and tags is crucial to Flashy, because this is the primary mode of content organization in the application. Henceforth, we will use the term "edge" to refer to a relation between a flashcard and a tag.
Design Requirements
The software requires that:
-
A card can be associated with many tags. For example, a card with a Physics question may be added to the "Physics" tag, and the "Midterm 2018" tag.
-
A tag can be associated with 1 or more cards. For example, The "Physics" tag may contain many Physics flashcards on different topics. We enforce that a tag must have at least one card, because a tag without cards is not useful and can be removed.
This relationship is illustrated in Class diagram describing association between flashcards and tags.:

Current Implementation
Edges are stored in an instance of CardTag
. CardTag
is an
association class, containing two `HashMap`s, as illustrated in Class Diagram illustrating relation between Card
, Tag
and CardTag
.

Card
, Tag
and CardTag
Note that CardTag is only weakly associated to Card and CardTag . In particular, it stores the string representation
of the IDs of Card and Tag objects. This can lead to inconsistent states: for example, it is valid code-wise to have CardTag
to contain IDs of Tag and Card objects that have been deleted. Defensive programming is added to ensure that their states are consistent and valid.
|
Similar to UniqueCardList
and UniqueTagList
, only one instance of CardTag
exists in each
CardBank
.
Exposed APIs
Interaction with the CardTag
instance is performed through the
Model
. The model exposes 3 main methods for creating and removing
new edges:
public interface Model { // ... /** Gets list of tags for a given card */ List<Tag> getTags(Card card); /** Removes the tags for a card */ void removeTags(Card card, Set<Tag> tags) throws EdgeNotFoundException, TagNotFoundException; /** Adds the tags for a card */ void addTags(Card card, Set<Tag> tags) throws DuplicateEdgeException; }
These three APIs are sufficient for Flashy’s design requirements as of
version 1.5. While the choice of APIs require some logic to be placed
in the commands (such as AddCardCommand
and EditCardCommand
), they
are reusable and their implementations can be easily changed.
The sequence diagrams for addTags
and removeTags
are shown in Sequence Diagram for addTags
and [fig::removeTags] respectively. In particular, it is important to note that:
-
During an
addTags
operation,-
The tags will be created if they do not exist
-
DuplicateEdgeException
is thrown when the flashcard is already associated with the tag
-
-
During a
removeTags
operation,-
TagNotFoundException
is thrown when the tag is not found in the card bank -
EdgeNotFoundException
is thrown when the flashcard and tag are not associated
-

addTags

removeTags
Design Considerations
Aspect: Storage of Edges
The edges should be persisted in storage. Hence, the CardTag
association class must be serializable to xml
. This limits the
design choices for CardTag
.
-
Alternative 1 (current choice): Add UUIDs to Cards and Tags, and model edges as UUID pairs.
-
Pros: This allows the
CardTag
instance to be easily, and cheaply serialized to and fromxml
. Reduces coupling between theCard
andTag
class. -
Cons: Requires the addition of a UUID field to the
Card
andTag
class. Developer needs to be careful with implementation, because UUID strings forCard
andTag
are indistinguishable. The additional code complexity is a result of storage being through serialization and deserialization of xml, and should be greatly reduced if a proper relational database and Object-Relational Mapping (ORM) tool were used (planned in v2.0).
-
-
Alternative 2: Store the edge information in
Card
andTag
.-
Pros: No need for additional UUID field. Lower estimated code complexity.
-
Cons: Strong coupling between the
Card
andTag
class. Requires heavy duplication of content in storage. The developer will also have to be careful with the implementation: editing an object would require changes in both theCard
object andTag
object.
-
Aspect: Choice of Data Structure
The application requires the following two operations to be fast:
-
Getting all
Card
instances from a specificTag
(operation 1) -
Getting all
Tag
instances from a specificCard
(operation 2)
Adjacency lists provide much better performance — O(1)
on both
operations, as compared to an adjacency matrix. In addition, a typical
CardTag
graph is sparse, and adjacency lists are much more memory
efficient. Hence, the adjacency list is a natural choice.
-
Alternative 1 (current choice): Storing edges in 2
HashMap`s: `cardMap
andtagMap
.-
Pros:
O(1)
for both operations. -
Cons: Double the memory requirements.
-
-
Alternative 2: Storing edges in 1
HashMap
:cardMap
ortagMap
.-
Pros: Uses minimal amount of memory.
-
Cons:
O(1)
only for one operation.
-
End of Extract