Introduction

The following document contains a more technical overview of the project. It is suited for developers who wish to develop the project further.

MeetBuddy is a desktop app optimised for people who want to organise their meetings and their contacts. They can store both meetings and contacts, view the meetings scheduled for the week, and take note of which contacts they are meeting up with. It also allows users to assign notes to their meetings to keep people informed about meeting details. Furthermore, it supports automatic syncing of contacts profile photos if they have a Gravatar account. The app is targeted towards users with a daily 7am - 4pm schedule, have frequent meetings during this period, and prefer typing.


Setting up, getting started

Refer to the guide Setting up and getting started.


Design

Architecture

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.

:bulb: Tip: The .puml files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.

Main has two classes called Main and MainApp. It is responsible for,

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.
  • At shut down: Shuts down the components and invokes cleanup methods where necessary.

Commons represents a collection of classes used by multiple other components.

The rest of the App consists of four components.

  • UI: The UI of the App.
  • Logic: The command executor.
  • Model: Holds the data of the App in memory.
  • Storage: Reads data from, and writes data to, the hard disk.

Each of the four components,

  • defines its API in an interface with the same personName as the Component.
  • exposes its functionality using a concrete {Component Name}Manager class (which implements the corresponding API interface mentioned in the previous point.

For example, the Logic component (see the class diagram given below) defines its API in the Logic.java interface and exposes its functionality using the LogicManager.java class which implements the Logic interface.

Class Diagram of the Logic Component

How the architecture components interact with each other

The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1.

The sections below give more details of each component.

UI component

Structure of the UI Component. Note that class diagram for TimetableView and MeetingDashboard are omitted
for clarity.

API : Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter etc. All these, including the MainWindow, inherit from the abstract UiPart class.

The UI component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml

The UI component,

  • Executes user commands using the Logic component.
  • Listens for changes to Model data so that the UI can be updated with the modified data.

Logic component

Structure of the Logic Component

API : Logic.java

  1. Logic uses the MeetBuddyParser class to parse the user command.
  2. This results in a Command object which is executed by the LogicManager.
  3. The command execution can affect the Model (e.g. adding a person).
  4. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.
  5. In addition, the CommandResult object can also instruct the Ui to perform certain actions, such as displaying help to the user.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete 1") API call.

Interactions Inside the Logic Component for the `delete 1` Command

:information_source: Note: The lifeline for DeletePersonCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

Model component

Structure of the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.
  • stores the MeetBuddy data.
  • exposes an unmodifiable ObservableList<Person> that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
  • stores the meeting book data.
  • exposes an unmodifiable ObservableList<Meeting> that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
  • stores the connection between the person in the contacts book and the meeting in the meeting book. e.g. Tom and July both participate in the CS2103 Lecture.
  • does not depend on any of the other three components.
  • (Some details are omitted)
:information_source: Note: An alternative (arguably, a more OOP) model is given below. It has a Tag list in the AddressBook, which Person references. This allows AddressBook to only require one Tag object per unique Tag, instead of each Person needing their own Tag object.
BetterModelClassDiagram

Storage component

Structure of the Storage Component. The full diagram for MeetingBook is omitted to make the figure less cluttered.

API : Storage.java

The Storage component,

  • can save UserPref objects in json format and read it back.
  • can save the MeetBuddy data in json format and read it back.

Common classes

Classes used by multiple components are in the seedu.addressbook.commons package.


Implementation

This section describes some noteworthy details on how certain features are implemented.

Sort feature (author: ToTo Tokaeo)

It can be helpful to sort the list of contacts and the list of meetings by a certain criteria. For example, sorting contacts by their names could complement the user experience. However, there were a few implementations details to consider. The implementations for sorting of the contacts and meetings are very similar, so I will only discuss the one for contacts.

Currently, an essential attribute in the model are the filtered lists, which show the results of searches and finds. The indexes used in commands like “edit” are taken with respect to these filtered list. These filtered lists, however, are backed by immutable observable lists, which helps update the JavaFX GUI. I initially tried to make copies of these observable lists, so that I can mutate them through sorting and filtering. However, this would not work, since commands like “edit” would be making changes to a copy of the data, not the data itself. So, initially, this filtered list was the access point for many other components, like the UI.

The original filter list diagram

The implementation I went with uses another subclass of observable lists called sorted lists. It goes in as the link between the original immutable observable list and the filtered lists. Sorting would occur in the sorted list layer, and the filtering will be applied on top. This has the benefit of still sharing the references with the original observable list, so modifications will still be reflected in the correct data structures.

After implementation

Unsort (author: ToTo Tokaeo)

To unsort, I simply remove the sorting comparator in the sorted list subclass. The list would become the original observable list.

Find meeting (author: ToTo Tokaeo)

The command findm leverages the filtering capabilities of the FilteredList for meetings. Hence, the only tricky part is to compute the predicate used to filter this FilteredList. The predicate is computed by the logical AND of all input predicates. For instance, suppose we want to search for a meeting with name “lecture” and with priority 1. The predicate to filter the meeting list is simply: meeting contains “lecture” in its name AND meeting has priority 1.

Show contacts in meeting (author: hengyiqun)

The command showm leverages on the PersonMeetingConnection as well as the FilteredPersonList for filtering. Specifically, a predicate, PersonInMeetingPredicate, is used to filter out persons who are in the specified meeting. The predicate can be extended to include people who are in groups that are within the meeting. This would involve the use of three streams - one stream for contacts directly associated with the meeting, one stream for all the groups of a person of interest, and a last stream for all groups in the meeting. This has been implemented, but a reduced version is currently used to show only contacts that are directly associated with the meeting. The expanded version will be introduced subsequently.

Given below is a Sequence Diagram for interactions within the Logic component for the execute("showm1") API call.

Interactions Inside the Logic Component for the `showm 1` Command

:information_source: Note: The lifeline for ShowMeetingCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

Find persons in group (author: hengyiqun)

The command findpg leverages the filtering capabilities of the FilteredList for persons. A predicate is used to compute if a user input keyword is part of a group.

Edit meeting (author: hengyiqun)

The command editm makes use of existing API to edit an existing meeting. As the initial meeting still exists in the MeetingBook, editing the meeting name will cause the application to register a clashing meeting (i.e. it clashes with itself due to the same start time and end time). A duplicate meeting will also be registered if only the priority of the meeting is edited, because only the name, start time and end time of a meeting are required to be unique. As such, additional API, clashesExceptOne has been implemented to ensure that meetings are not checked against itself for clashes and duplicates.

Below shows a general Sequence Diagram for editm 1. Details for timetable conflicts and duplicates have been deliberately left out for brevity.

Sequence Diagram for the `editm 1` Command

Notes feature (author: hengyiqun)

(Coming Soon : v1.5 has already been implemented, but will only be introduced in subsequent iterations)

The note feature has been implemented to help the user insert and delete personal notes. This feature is introduced with the intention to help users manage their personal notes, and to make MeetBuddy a more attractive one-stop application for users to manage their contacts, store their meeting information, and also keep track of various personal notes. While this feature has already been implemented, it will only be made available in subsequent iterations, after the storage is made more mature for handling notes.

Just like the AddressBook for managing contacts and MeetingBook for managing meeting information, a NoteBook was implemented for users to store personal notes. The NoteBook is implemented with a Command Pattern.

The user input command is first passed to the LogicManager class, which then makes use of the MeetBuddyParser for deciding the type of command. Commands can be related to contacts, meetings, notes or timetable. This is decided based on the command word. A specific command parser, such as AddNoteCommandParser, will be called which return a Command such as AddNoteCommand. This command will be executed by the Logic Manager, before encapsulating the result as a CommandResult object for passing back to the Model. The Ui component may then be invoked to display the results (e.g. the notes) to the user.

Timetable feature (author : Maurice)

The timetable feature will be help the user visualise the free times, as well as his/her meetings schedule for the following week. It also aids the user in scheduling meetings faster or finding dates that she will be free on. It should be responsive, such that when the user edits, adds or deletes a meeting, it should be reflected in the timetable, if that meeting falls within the timetable range. The user should be able to at a glance tell what meetings she has on a particular day in the timetable, and the timeslot of the meeting. The user should be able to change the start date of the timetable. This allows to view a wider range of days, as well as view past week schedules.

Implementation

To display the weekly schedule of the user, we first implement a timetable grid Ui component that would represent the view of the timetable. A JavaFX GridPane layout is chosen, with 2 rows and 7 columns. Each column would represent the days in the timetable. The first row would display the date labels for each row, and the second row would be the main timetable view.

Each meeting in our model can be represented as a slot in our timetable. The length of the slot will be scaled linearly with the meeting duration. When displayed, the slot should show the duration of the meeting and the timeframe the slot it is scheduled on. We implement each slot as a VBox with a text label.

The meeting slots should be then slotted into the timetable according to the start times and date. Therefore each column in our main view should have an anchorPane layout, which allows us control over the positioning of the nodes in the timetable.

A view of the timetable GUI

Using a MVC pattern, the TimetableView Class should act as the Controller logic for positioning the nodes, as well as creating the timetable slots of appropriate size, given the list of meetings to schedule.

Since the meetings have a well-defined start and end time, we create an interface called Schedulable, and make the Meetings class implement that interface. This would allow us to program to an interface, rather than a class, thus leading to less coupling. In that regard, now a timetable’s task would be rendering schedulable objects on its GUI.

Alternative 1: Park all the logic under the TimetableView Class. It will be responsible for processing the data on the Schedulables and rendering it on the timetable UI.

  1. Pros: Less overhead.
  2. Cons: Ties the implementation strategy of data processing, node placement and resizing heavily to the TimetableView class, thus leading to more work to be done for future developers to change the strategy.

Alternative 2 (current choice): Delegate the logic of node resizing and positioning to a separate class.

  1. Pros: More overhead.
  2. Cons: Allows for easier modification in the future if another developer wants to change how the nodes are placed on the timetable, as he just needs to override timetablePolicy class. Allows for the timetable behavior to change dynamically (during runtime, say if a person enters a command to display), simply by setting to a new timetablePolicy class.

We choose 2 because it makes the code neater and separates out the responsibilities nicely. It also keeps future maintainability in mind.

A view of the timetable GUI

The next feature was to make the TimetableView class responsive to user commands and changes to the data in the model. There are two ways this can be done.

Alternative 1: Allow a boolean in CommandResult to indicate whether timetable should be updated. The Ui will then update accordingly.

  1. Pros: Allows more precision over when to update the model. Instead of updating at every change, the Command logic will do the job of determining if the timetable is necessary to be updated, thus potentially making it faster.
  2. Cons: Introduces high coupling between Timetable class and every Command, would introduce burden on future developers. Additionally, it would introduce more costs to unit test.

Alternative 2 (current choice): Using an observer pattern, update the timetable whenever the underlying model changes.

  1. Pros: The update command is being executed more times, often unnecessarily. For example, when user adds a date outside timetable range.
  2. Cons: Allows for greater decoupling between the logic and model due to the nature of the Observer Pattern.

Overall Design Chosen

These were the overall final design considerations.

Two possible implementations were considered.

Alternative 1 : Represent a seven-day schedule as a 2 dimensional array with each cell representing a 30 minute time interval, with each row representing a day, and each column representing a specified timeslot within the day. Similar to a booking system, whenever a meeting is added or deleted, it will mark the timeslots as taken, or free up the slots.

  1. Pros: A Ui can listen to the model and the display can be updated quickly with each cell change. Whenever a meeting is added or removed, it only takes O(1) time to check each timeslot occupied by the meeting.

  2. Cons: Takes up more space. Needs a lot of restrictions on handling meetings with not nice start and ending times, and thus reduces a lot of flexibility for the user.

Alternative 2 (*Current Choice): Represent each day as a column in the GUI, for a total of 7 columns. Each meeting will be represented as a slot of length proportional to the meeting duration. Depending on the meeting’s start times and end times, the program will determine which column to slot the meeting in, as well as the vertical position in the column to put the meeting.

  1. Pros: More flexible in allowing users to display meetings with much more different start end times. Takes up less space in the model.

  2. Cons: It takes up more time for each operation to update the GUI, because you have to update at the very least a whole column, or search through the slots in the column to find the slot.

Implementation : setTimetable

We store an SimpleObjectProperty of a localDate inside a class called timetablePrefs in the model. When we initialize the Ui components, a ReadOnlyObservableValue of this localDate is passed to the TimetableView. TimetableView will then attach a listener to this ReadOnlyObservableValue. We program to the ReadOnlyObservableValue interface here as a form of defensive programming, as we do not want to alter the observableValue outside of the setTimetableCommand.

The following sequence diagram illustrates what happens when setTimetableCommand is called with a valid date.

A sequence diagram of the setTimetable Command

Users can set the timetable to update to start on any date using the command setTimetable [DATE]. Like before, it also uses the Observer Pattern, and the design considerations were similar to earlier. Below is a sequence diagram of how the timetable is updated.

A sequence diagram of the setTimetable Command

Some small considerations were considered in the implementation, for example, if the user leaves the date field empty, it should default set to today’s date. The following activity diagram shows the setTimetableCommand being parsed.

A sequence diagram of the setTimetable Command

Person Meeting Connection feature (Written by: Chen Yuheng (Github: skinnychenpi))

MeetBuddy allows users to track and record the contacts related with their meetings.

Implementation

A PersonMeetingConnection class stores all of the relevant information of persons in the contact related to certain meetings. The class diagram below shows how all the different components interact to allow the person meeting connection feature to function. Note that the XYZConnectionCommand and XYZConnectionCommandParser refers to all Connection related commands like add, delete etc. UndoRedoState0 A PersonMeetingConnection(PMC) slot is represented by the PMC class which contains 2 key attributes, personsInMeeting and meetingsInPerson. Both of them are HashMaps. The attribute personsInMeeting is a hashmap whose key is a Meeting object and its value is a UniquePersonList. Another attribute meetingsInPerson is reversed, whose key is a Person object and its value is a UniqueMeetingList.

The XYZPMCCommand class represent classes that extend the abstract class Command and allows the users to add and delete the PMC to MeetBuddy. These XYZPMCCommands are created by the respective XYZPMCCommandParsers.

The PMC object is unique in the model, and it stores all the connections.

Given the class diagram and the understanding of how the PMC class interacts with other classes, let us examine how an addPersonMeetingConnection command behaves, by using the following activity diagram of the system.

UndoRedoState0

The workflow above shows how a connection is added and the various checks that occurs at the different points throughout the workflow.

The workflow for deleting connection is similar as above, except that it has one more step to check whether the person the user wants to delete exist in the meeting.

To summarize the above activity diagram, there are several key checks which MeetBuddy checks for when the user is adding a connection. Firstly, MeetBuddy checks if the index for the meeting and the prefix for person (p/) are presented in the command. Also, MeetBuddy checks if all prefixes present are formatted correctly. Then, it checks if the Meeting index provided is within the range of the Meeting list. Following which, MeetBuddy will check if the persons’ indices passed for the prefixes are within the range.

Design Considerations

Create a Person Meeting Connection Class or Add new attributes into Person and Meeting (i.e Add meetingsRelated attribute into Person Class).

  Alternative 1 (Current Choice): Create a Person Meeting Connection Class Alternative 1 : Add new attribute into Person and Meeting class
Pros Don’t need to modify the signatures for Person and Meeting classes’ constructors and APIs. Reduce level of coupling for both classes. Easier to manage and access connection. The GUI is shown based on a meeting card, hence using this way is easier to shown as the personsRelated is stored in a meeting object. Easier for storage implementation.
Cons More difficult to manage storage, need to take care of the effect to PMC when either the meetings or persons changes. Increase the level of coupling and need to modify the codebase in a large scale.

Reason for choosing Alternative 1: Due to the time constraint of this project, our group has decided to choose alternative 1, as it not only reduces coupling, but is sufficient for us to uniquely identify the Persons Related participating in the meeting.


Documentation, logging, testing, configuration, dev-ops


Appendix: Requirements

Product scope

Target user profile:

  • Any user who like typing, and is most of the week spent moving about meeting people for his internship, lectures, or social life, have busy schedules from 7 am - 4pm.
  • Has a lot of contacts and meeting many people out for events.
  • Is more visual and would like to see the meetings on a timetable, and see when he is free. Is probably relatively young, and has friends with online gravatar profiles.
  • Would like to keep track of contacts and organise them as well for easy searching and easy remembering.
  • Can type fast
  • Prefers typing to mouse interactions
  • Is reasonably comfortable using CLI apps

Value proposition: manage contacts faster than a typical mouse/GUI driven app.

Can manage social life and academics by toggling between two modes <-> school activities and non-school activities. Better time management - Priorities of meetups can be ranked and less time to schedule meetings with friends, as well as keep track of existing meetings. Arrange activities with many people -> events not only tie with single contacts but with a group of contacts that can be added inside. Stay connected -> Keep in touch with old contacts or remove them by querying for old contacts . Keep a log and diary of past meetups, and small bios of people ,as well as images.

Users would be better able to manage their social and academic commitments by toggling between both modes. With the option to rank/prioritise meetups, users can experience better time management, and can stay connected with many people easily. The app also maintains a diary of past meetups, with the inclusion of images and bios of the people.

Appendix: User stories

Priority As a I can so that
high typing guy use command line interface I don’t have to click around
high student give meeting priorities I can prioritize my time
high busy student Look up my old contacts quickly I can reach out to them for a catch up
high visual person Add picture to a contact or group I can remember who they are
medium person trying to back up data store information in a file I can clear and move my data to a different machine
medium student with a lot of groups assign contacts to groups I can organise my contacts into the groups
high user close the app I can clear up my memory and store the data into text files
high user add meetings I can keep track of my meetings
medium user look up meeting by time I can tell if I’m busy during a time
medium user sort contacts I can organize the contact list in a convenient way
medium user sort meetings I can organize the meeting list in a convenient way
high user view all contacts I can see all the contacts I added
high user view all meetings I can see all the meetings I added
high user delete meetings I can remove the meetings that already happened
high user delete contacts I can remove the unimportant contacts

Appendix: Use cases

(For all use cases below, the System is the MeetBuddy and the Actor is the user, unless specified otherwise)

Use case: Delete a person

MSS

  1. User requests to list persons
  2. MeetBuddy shows a list of persons
  3. User requests to delete a specific person in the list
  4. MeetBuddy deletes the person Use case ends

Extensions

  • 2a. The list is empty.

    Use case ends.

  • 3a. The given index is invalid.

    • 3a1. MeetBuddy shows an error message.

      Use case resumes at step 2.

Use case: Assigning priorities to meetings

MSS

  1. User requests to add a meeting with priority
  2. MeetBuddy shows the list of meetings after adding.

    Use case ends

Extensions

  • 1a. The priority is out of range.

    • 1a1. MeetBuddy shows an error message.

    Use case ends.

Use case: Sort contact list

MSS

  1. User requests to sort the contact list
  2. MeetBuddy updates the contact list following the sorting criterion.

    Use case ends.

Extensions

  • 1a. The sort parameters are invalid

    • 1a1. MeetBuddy shows an error message.

    Use case ends.

Use case: Unsort contact list

MSS

  1. User requests to unsort the contact list
  2. MeetBuddy updates the contact list to the original state without sorting.

    Use case ends.

Use case: Sort meeting list

MSS

  1. User requests to sort the meeting list
  2. MeetBuddy updates the meeting list following the sorting criterion.

    Use case ends.

Extensions

  • 1a. The sort parameters are invalid

    • 1a1. MeetBuddy shows an error message.

    Use case ends.

Use case: Unsort contact list

MSS

  1. User requests to unsort the meeting list
  2. MeetBuddy updates the meeting list to the original state without sorting.

    Use case ends.

Use case: Find meeting

MSS

  1. User searches for a meeting with some criteria.
  2. MeetBuddy updates the meeting list following the criteria.

    Use case ends.

Extensions

  • 1a. The search parameters are invalid

    • 1a1. MeetBuddy shows an error message.

    Use case ends.

  • 2a. No meeting is found.

    • 2a1. MeetBuddy shows an empty meeting list.

    Use case ends.

Appendix: Non-Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 11 or above installed.
  2. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
  3. The response to any use action should become visible within 2 seconds.
  4. The source code should be open source.
  5. There should not be noticeable lag in the UI when handling up to 100 people.

Appendix: Glossary

  • Mainstream OS: Windows, Linux, Unix, OS-X
  • User: the person using the MeetBuddy application
  • Contact: a person that the user wants to keep track of, especially by adding the person into the app
  • Person: used interchangeably with contact
  • Contact List: a list of people that the user have added into the app
  • Meeting: any event with a time that the user wants to keep track of, especially by adding the event into the app.
  • Meeting List : a list of meetings that the user have added into the app

Appendix: Instructions for manual testing

Given below are instructions to test the app manually.

:information_source: Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

Launch and shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

Deleting a person

  1. Deleting a person while all persons are being shown

    1. Prerequisites: List all persons using the list command. Multiple persons in the list.

    2. Test case: delete 1
      Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete, delete x, ... (where x is larger than the list size)
      Expected: Similar to previous.

Adding a person

  1. Add a person using the command addp following instructions from the User Guide.

  2. Add a person using an invalid command like addp nonsense should yield an error, and an error message.

  3. Add a person using an invalid field type like addp n/name ph/crazy should yield an error, and an error message. Here, the phone number is invalid.

Finding a person by name

  1. Find a person by name using the command findp following instructions from the User Guide.

  2. If no such person is found, the contact list should be empty. Do listp to get back a contact list.

Finding a person by group

  1. Find a person by group using the command findpg following instructions from the User Guide.

  2. If no such person is found, the contact list should be empty. Do listp to get back a contact list.

Sort the contact list

  1. Sort the contact list by using the command sortp following instructions from the User Guide.

  2. If the instructions are followed correctly, the contact list should be updated.

Unsort the contact list

  1. Sort the contact list by using the commandunsortp following instructions from the User Guide.

  2. The contact list should return to the original ordering, that is the order the contacts were added in.

Perform sort, unsort, add, delete for meeting

  1. Follow the instructions for the sort, unsort, add, delete commands for meetings from the User Guide. Usually, the command involves a trailing m instead of p.

Find a meeting

  1. Find meeting by using the command findm following instructions from the User Guide. Note that this findm does much more than its contact list counterparts.

  2. An error should appear for invalid inputs, such as findm n/name time/sometime, which has an invalid time input.

  3. If the command goes through, the meeting list will be filtered to show only the meetings that fulfill all the search criteria. If no meetings are shown, then no meetings fulfill all the search criteria

Adding a person to a meeting

  1. Add a person to a meeting by using the command addptm following instructions from the User Guide. Note that this person must be part of the contact list.

  2. If the command goes through, this person’s name should appear in the meeting card.

Deleting a person from a meeting

  1. Delete a person from a meeting by using the command deletepfm following instructions from the User Guide.

  2. If the command goes through, the person’s name should no longer appear on the meeting.

Show people in a meeting

  1. Show people in a meeting by using the command showm following instructions from the User Guide.

  2. If the command goes through, only the people in the specified meeting will be shown in the contact list. The command listp will bring the entire contact list back.

Inspect timetable

  1. Click the Timetable tab in the GUI. More details can be found in the User Guide.