# Contributing to ClusterView ## PR Requirements Before a PR is accepted it must pass the following requirements in CircleCI: - [ ] All tests must pass - [ ] The code must be lint free as determined by `flake8` Additionally, any new code included in the PR must be tested fully. ## Developer Requirements You will want to make sure that all developer requirements are installed. To do this after creating a new `virtualenv` environment: ```sh pip3 install -r requirements-dev.txt ``` This will install all normal requirements as well as testing requirements. ## Linting We use `flake8` for linting. Run `python -m flake8` in order to get a lint report. ## Running Tests We use `pytest` as our testing framework. To run tests with test coverage use `pytest --cov=clusterview tests/`. ## Updating the UI Design File In order to load the UI and be able to interact with the various parts you will need to convert the UI file to a python file. To do so: ```sh pyuic5 .ui -o .py ``` For example, to be able to use changes you've made to the main UI file: ``` pyuic5 clusterview.ui clusterview_ui.py ``` ## Style ### Import Organization Import organization is a critical part of good software engineering. In large projects considerable time can be wasted looking for a specific import. The imports must be organized as follows: ```python # System imports first # Third party imports second # UNLV/ClusterView modules external to the current module third # Modules local to the current module last ``` Additionally, each imported module will be alphabetized by the module name inside of the `import X` (X being the module name) or the `from X import Y` (X being the module name). If you are importing more than one thing from a module, alphabetize those as well `from x import foo, bar, baz` should be `from x import bar, baz, foo`. ### Prefer Private Methods In Python, private methods don't exist in the way that they do in C++, Java, etc. To declare a method private we must tell the compiler to garble the name. To do this add two underscores to the prefix of the method name: ```python # This method can be seen by anyone on import. def hello_world(): print("Hello, world!") # This method can only be seen by the module/class it is defined # in. def __hello_module(): print("Hello, module!") ``` We only want to expose the absolute bare minimum number of functions to the consumers of our modules. ## Overview of Function In order to understand how things are drawn and maintained there are some classes you need to know. ### Modes Lambas, partially applied functions, and function passing are used throughout the application to not only improve readability but also improve modularity of the code. You can see modes in `clusterview/mode.py`. These modes are used to put the application in a global state for adding, editing, moving, and deleting things. In order to improve readability `main_window.py`, the main application class module, uses some neat function passing tricks to determine which modes to use in `clusterview/mode_handlers.py`. The `mode_handlers.py` module is fairly complicated but defines basically all of the mode handling code used for each mode in the application. ### Point The `Point` class is the base class for all drawn points. It contains important details for calculating location in space such as it's containing viewport, it's x and y coordinates in the viewport, it's drawn size, it's color, and a list of optional attributes. ### PointSet A `PointSet` is a convenient holder for all points on a screen. It contains methods that simplify adding to, removing, moving, and deleting points from the canvas. It maintains a constant viewport state, point size, and other useful attributes so that you do not need to concern yourself with them during use. ### PointManager The `PointManager` is a global singleton that is shared across the entire application. You can think of the `PointManager` as the absolute source of truth for the state of points in the application. In addition to maintaining a global `PointSet`, it also maintains the `save` and `load` configuration features so that they are consistent each time they are used. ### Grouping Points Each point can be grouped by color, defined in `clusterview/colors.py` in the global `PointSet`. This way you do not need to explicitly maintain sets of points representing groups, and can instead call the `groups` function on the `PointSet` to return to you a dictionary from `color.Color` to `Point` representing each group and their member points.