# YAPF

## Introduction

YAPF is a Python formatter based on [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html)
(developed by Daniel Jasper). In essence, the algorithm takes the code and
calculates the best formatting that conforms to the configured style. It takes
away a lot of the drudgery of maintaining your code.

The ultimate goal is that the code YAPF produces is as good as the code that a
programmer would write if they were following the style guide.

> **Note**
> YAPF is not an official Google product (experimental or otherwise), it is
> just code that happens to be owned by Google.

## Installation

To install YAPF from PyPI:

```bash
$ pip install yapf
```

YAPF is still considered in "beta" stage, and the released version may change
often; therefore, the best way to keep up-to-date with the latest development
is to clone this repository or install directly from github:

```bash
$ pip install git+https://github.com/google/yapf.git
```

Note that if you intend to use YAPF as a command-line tool rather than as a
library, installation is not necessary. YAPF supports being run as a directory
by the Python interpreter. If you cloned/unzipped YAPF into `DIR`, it's
possible to run:

```bash
$ PYTHONPATH=DIR python DIR/yapf [options] ...
```

## Using YAPF within your favorite editor
YAPF is supported by multiple editors via community extensions or plugins. See [Editor Support](EDITOR%20SUPPORT.md) for more info.

## Required Python versions

YAPF supports Python 3.7+.

## Usage

```console
usage: yapf [-h] [-v] [-d | -i | -q] [-r | -l START-END] [-e PATTERN]
            [--style STYLE] [--style-help] [--no-local-style] [-p] [-m] [-vv]
            [files ...]

Formatter for Python code.

positional arguments:
  files                 reads from stdin when no files are specified.

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -d, --diff            print the diff for the fixed source
  -i, --in-place        make changes to files in place
  -q, --quiet           output nothing and set return value
  -r, --recursive       run recursively over directories
  -l START-END, --lines START-END
                        range of lines to reformat, one-based
  -e PATTERN, --exclude PATTERN
                        patterns for files to exclude from formatting
  --style STYLE         specify formatting style: either a style name (for
                        example "pep8" or "google"), or the name of a file
                        with style settings. The default is pep8 unless a
                        .style.yapf or setup.cfg or pyproject.toml file
                        located in the same directory as the source or one of
                        its parent directories (for stdin, the current
                        directory is used).
  --style-help          show style settings and exit; this output can be saved
                        to .style.yapf to make your settings permanent
  --no-local-style      don't search for local style definition
  -p, --parallel        run YAPF in parallel when formatting multiple files.
  -m, --print-modified  print out file names of modified files
  -vv, --verbose        print out file names while processing
```

### Return Codes

Normally YAPF returns zero on successful program termination and non-zero
otherwise.

If `--diff` is supplied, YAPF returns zero when no changes were necessary,
non-zero otherwise (including program error). You can use this in a CI workflow
to test that code has been YAPF-formatted.

### Excluding files from formatting (.yapfignore or pyproject.toml)

In addition to exclude patterns provided on commandline, YAPF looks for
additional patterns specified in a file named `.yapfignore` or `pyproject.toml`
located in the working directory from which YAPF is invoked.

`.yapfignore`'s syntax is similar to UNIX's filename pattern matching:

```
*       matches everything
?       matches any single character
[seq]   matches any character in seq
[!seq]  matches any character not in seq
```

Note that no entry should begin with `./`.

If you use `pyproject.toml`, exclude patterns are specified by `ignore_patterns` key
in `[tool.yapfignore]` section. For example:

```ini
[tool.yapfignore]
ignore_patterns = [
  "temp/**/*.py",
  "temp2/*.py"
]
```

Formatting style
================

The formatting style used by YAPF is configurable and there are many "knobs"
that can be used to tune how YAPF does formatting. See the `style.py` module
for the full list.

To control the style, run YAPF with the `--style` argument. It accepts one of
the predefined styles (e.g., `pep8` or `google`), a path to a configuration
file that specifies the desired style, or a dictionary of key/value pairs.

The config file is a simple listing of (case-insensitive) `key = value` pairs
with a `[style]` heading. For example:

```ini
[style]
based_on_style = pep8
spaces_before_comment = 4
split_before_logical_operator = true
```

The `based_on_style` setting determines which of the predefined styles this
custom style is based on (think of it like subclassing). Four
styles are predefined:

- `pep8` (default)
- `google` (based off of the [Google Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md))
- `yapf` (for use with Google open source projects)
- `facebook`

See `_STYLE_NAME_TO_FACTORY` in [`style.py`](https://github.com/google/yapf/blob/main/yapf/yapflib/style.py) for details.

It's also possible to do the same on the command line with a dictionary. For
example:

```bash
--style='{based_on_style: pep8, indent_width: 2}'
```

This will take the `pep8` base style and modify it to have two space
indentations.

YAPF will search for the formatting style in the following manner:

1. Specified on the command line
2. In the `[style]` section of a `.style.yapf` file in either the current
   directory or one of its parent directories.
3. In the `[yapf]` section of a `setup.cfg` file in either the current
   directory or one of its parent directories.
4. In the `[tool.yapf]` section of a `pyproject.toml` file in either the current
   directory or one of its parent directories.
5. In the `[style]` section of a `~/.config/yapf/style` file in your home
   directory.

If none of those files are found, the default style PEP8 is used.

Example
=======

An example of the type of formatting that YAPF can do, it will take this ugly
code:

```python
x = {  'a':37,'b':42,

'c':927}

y = 'hello ''world'
z = 'hello '+'world'
a = 'hello {}'.format('world')
class foo  (     object  ):
  def f    (self   ):
    return       37*-+2
  def g(self, x,y=42):
      return y
def f  (   a ) :
  return      37+-+a[42-x :  y**3]
```

and reformat it into:

```python
x = {'a': 37, 'b': 42, 'c': 927}

y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}'.format('world')

class foo(object):
    def f(self):
        return 37 * -+2

    def g(self, x, y=42):
        return y

def f(a):
    return 37 + -+a[42 - x:y**3]
```

## Example as a module

The two main APIs for calling YAPF are `FormatCode` and `FormatFile`, these
share several arguments which are described below:

```python
>>> from yapf.yapflib.yapf_api import FormatCode  # reformat a string of code

>>> formatted_code, changed = FormatCode("f ( a = 1, b = 2 )")
>>> formatted_code
'f(a=1, b=2)\n'
>>> changed
True
```

A `style_config` argument: Either a style name or a path to a file that
contains formatting style settings. If None is specified, use the default style
as set in `style.DEFAULT_STYLE_FACTORY`.

```python
>>> FormatCode("def g():\n  return True", style_config='pep8')[0]
'def g():\n    return True\n'
```

A `lines` argument: A list of tuples of lines (ints), [start, end], that we
want to format. The lines are 1-based indexed. It can be used by third-party
code (e.g., IDEs) when reformatting a snippet of code rather than a whole file.

```python
>>> FormatCode("def g( ):\n    a=1\n    b = 2\n    return a==b", lines=[(1, 1), (2, 3)])[0]
'def g():\n    a = 1\n    b = 2\n    return a==b\n'
```

A `print_diff` (bool): Instead of returning the reformatted source, return a
diff that turns the formatted source into reformatted source.

```diff
>>> print(FormatCode("a==b", filename="foo.py", print_diff=True)[0])
--- foo.py (original)
+++ foo.py (reformatted)
@@ -1 +1 @@
-a==b
+a == b
```

Note: the `filename` argument for `FormatCode` is what is inserted into the
diff, the default is `<unknown>`.

`FormatFile` returns reformatted code from the passed file along with its encoding:

```python
>>> from yapf.yapflib.yapf_api import FormatFile  # reformat a file

>>> print(open("foo.py").read())  # contents of file
a==b

>>> reformatted_code, encoding, changed = FormatFile("foo.py")
>>> formatted_code
'a == b\n'
>>> encoding
'utf-8'
>>> changed
True
```

The `in_place` argument saves the reformatted code back to the file:

```python
>>> FormatFile("foo.py", in_place=True)[:2]
(None, 'utf-8')

>>> print(open("foo.py").read())  # contents of file (now fixed)
a == b
```

## Formatting diffs

Options:

```console
usage: yapf-diff [-h] [-i] [-p NUM] [--regex PATTERN] [--iregex PATTERN][-v]
                 [--style STYLE] [--binary BINARY]

This script reads input from a unified diff and reformats all the changed
lines. This is useful to reformat all the lines touched by a specific patch.
Example usage for git/svn users:

  git diff -U0 --no-color --relative HEAD^ | yapf-diff -i
  svn diff --diff-cmd=diff -x-U0 | yapf-diff -p0 -i

It should be noted that the filename contained in the diff is used
unmodified to determine the source file to update. Users calling this script
directly should be careful to ensure that the path in the diff is correct
relative to the current working directory.

optional arguments:
  -h, --help            show this help message and exit
  -i, --in-place        apply edits to files instead of displaying a diff
  -p NUM, --prefix NUM  strip the smallest prefix containing P slashes
  --regex PATTERN       custom pattern selecting file paths to reformat
                        (case sensitive, overrides -iregex)
  --iregex PATTERN      custom pattern selecting file paths to reformat
                        (case insensitive, overridden by -regex)
  -v, --verbose         be more verbose, ineffective without -i
  --style STYLE         specify formatting style: either a style name (for
                        example "pep8" or "google"), or the name of a file
                        with style settings. The default is pep8 unless a
                        .style.yapf or setup.cfg or pyproject.toml file
                        located in the same directory as the source or one of
                        its parent directories (for stdin, the current
                        directory is used).
  --binary BINARY       location of binary to use for YAPF
```

## Python features not yet supported
* Python 3.12 – [PEP 695 – Type Parameter Syntax](https://peps.python.org/pep-0695/) – [YAPF #1170](https://github.com/google/yapf/issues/1170)
* Python 3.12 – [PEP 701 – Syntactic formalization of f-strings](https://peps.python.org/pep-0701/) – [YAPF #1136](https://github.com/google/yapf/issues/1136)

## Knobs

#### `ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT`

>    Align closing bracket with visual indentation.

#### `ALLOW_MULTILINE_LAMBDAS`

>    Allow lambdas to be formatted on more than one line.

#### `ALLOW_MULTILINE_DICTIONARY_KEYS`

>    Allow dictionary keys to exist on multiple lines. For example:

```python
    x = {
        ('this is the first element of a tuple',
         'this is the second element of a tuple'):
             value,
    }
```

#### `ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS`

>    Allow splitting before a default / named assignment in an argument list.

#### `ALLOW_SPLIT_BEFORE_DICT_VALUE`

>    Allow splits before the dictionary value.

#### `ARITHMETIC_PRECEDENCE_INDICATION`

>    Let spacing indicate operator precedence. For example:

```python
    a = 1 * 2 + 3 / 4
    b = 1 / 2 - 3 * 4
    c = (1 + 2) * (3 - 4)
    d = (1 - 2) / (3 + 4)
    e = 1 * 2 - 3
    f = 1 + 2 + 3 + 4
```

>    will be formatted as follows to indicate precedence:

```python
    a = 1*2 + 3/4
    b = 1/2 - 3*4
    c = (1+2) * (3-4)
    d = (1-2) / (3+4)
    e = 1*2 - 3
    f = 1 + 2 + 3 + 4
```

#### `BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION`

>    Sets the number of desired blank lines surrounding top-level function and
>    class definitions. For example:

```python
    class Foo:
        pass
                       # <------ having two blank lines here
                       # <------ is the default setting
    class Bar:
        pass
```

#### `BLANK_LINE_BEFORE_CLASS_DOCSTRING`

>    Insert a blank line before a class-level docstring.

#### `BLANK_LINE_BEFORE_MODULE_DOCSTRING`

>    Insert a blank line before a module docstring.

#### `BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF`

>    Insert a blank line before a `def` or `class` immediately nested within
>    another `def` or `class`. For example:

```python
    class Foo:
                       # <------ this blank line
        def method():
            pass
```

#### `BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES`

>    Sets the number of desired blank lines between top-level imports and
>    variable definitions. Useful for compatibility with tools like isort.

#### `COALESCE_BRACKETS`

>    Do not split consecutive brackets. Only relevant when
>    `DEDENT_CLOSING_BRACKETS` or `INDENT_CLOSING_BRACKETS` is set. For example:

```python
    call_func_that_takes_a_dict(
        {
            'key1': 'value1',
            'key2': 'value2',
        }
    )
```

>    would reformat to:

```python
    call_func_that_takes_a_dict({
        'key1': 'value1',
        'key2': 'value2',
    })
```

#### `COLUMN_LIMIT`

>    The column limit (or max line-length)

#### `CONTINUATION_ALIGN_STYLE`

>    The style for continuation alignment. Possible values are:

>    - `SPACE`: Use spaces for continuation alignment. This is default
>      behavior.
>    - `FIXED`: Use fixed number (`CONTINUATION_INDENT_WIDTH`) of columns
>      (i.e. `CONTINUATION_INDENT_WIDTH`/`INDENT_WIDTH` tabs or
>      `CONTINUATION_INDENT_WIDTH` spaces) for continuation alignment.
>    - `VALIGN-RIGHT`: Vertically align continuation lines to multiple of
>      `INDENT_WIDTH` columns. Slightly right (one tab or a few spaces) if cannot
>      vertically align continuation lines with indent characters.

#### `CONTINUATION_INDENT_WIDTH`

>    Indent width used for line continuations.

#### `DEDENT_CLOSING_BRACKETS`

>    Put closing brackets on a separate line, dedented, if the bracketed
>    expression can't fit in a single line. Applies to all kinds of brackets,
>    including function definitions and calls. For example:

```python
    config = {
        'key1': 'value1',
        'key2': 'value2',
    }  # <--- this bracket is dedented and on a separate line

    time_series = self.remote_client.query_entity_counters(
        entity='dev3246.region1',
        key='dns.query_latency_tcp',
        transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
        start_ts=now()-timedelta(days=3),
        end_ts=now(),
    )  # <--- this bracket is dedented and on a separate line
```

#### `DISABLE_ENDING_COMMA_HEURISTIC`

>    Disable the heuristic which places each list element on a separate line if
>    the list is comma-terminated.
>
>    Note: The behavior of this flag changed in v0.40.3.  Before, if this flag
>    was true, we would split lists that contained a trailing comma or a
>    comment.  Now, we have a separate flag, `DISABLE_SPLIT_LIST_WITH_COMMENT`,
>    that controls splitting when a list contains a comment.  To get the old
>    behavior, set both flags to true.  More information in
>    [CHANGELOG.md](CHANGELOG.md#new-disable_split_list_with_comment-flag).

#### `DISABLE_SPLIT_LIST_WITH_COMMENT`

>    Don't put every element on a new line within a list that contains
>    interstitial comments.
>
>    Without this flag (default):
>
>    ```
>    [
>      a,
>      b,  #
>      c
>    ]
>    ```
>
>    With this flag:
>
>    ```
>    [
>      a, b,  #
>      c
>    ]
>    ```
>
>    This mirrors the behavior of clang-format and is useful for forming
>    "logical groups" of elements in a list.  It also works in function
>    declarations.

#### `EACH_DICT_ENTRY_ON_SEPARATE_LINE`

>    Place each dictionary entry onto its own line.

#### `FORCE_MULTILINE_DICT`

>    Respect `EACH_DICT_ENTRY_ON_SEPARATE_LINE` even if the line is shorter than
>    `COLUMN_LIMIT`.

#### `I18N_COMMENT`

>    The regex for an internationalization comment. The presence of this comment
>    stops reformatting of that line, because the comments are required to be
>    next to the string they translate.

#### `I18N_FUNCTION_CALL`

>    The internationalization function call names. The presence of this function
>    stops reformatting on that line, because the string it has cannot be moved
>    away from the i18n comment.

#### `INDENT_BLANK_LINES`

>    Set to `True` to prefer indented blank lines rather than empty

#### `INDENT_CLOSING_BRACKETS`

>    Put closing brackets on a separate line, indented, if the bracketed
>    expression can't fit in a single line. Applies to all kinds of brackets,
>    including function definitions and calls. For example:

```python
    config = {
        'key1': 'value1',
        'key2': 'value2',
        }  # <--- this bracket is indented and on a separate line

    time_series = self.remote_client.query_entity_counters(
        entity='dev3246.region1',
        key='dns.query_latency_tcp',
        transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
        start_ts=now()-timedelta(days=3),
        end_ts=now(),
        )  # <--- this bracket is indented and on a separate line
```

#### `INDENT_DICTIONARY_VALUE`

>    Indent the dictionary value if it cannot fit on the same line as the
>    dictionary key. For example:

```python
    config = {
        'key1':
            'value1',
        'key2': value1 +
                value2,
    }
```

#### `INDENT_WIDTH`

>    The number of columns to use for indentation.

#### `JOIN_MULTIPLE_LINES`

>    Join short lines into one line. E.g., single line `if` statements.

#### `NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS`

>    Do not include spaces around selected binary operators. For example:

```python
    1 + 2 * 3 - 4 / 5
```

>    will be formatted as follows when configured with `*`, `/`:

```python
    1 + 2*3 - 4/5
```

#### `SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET`

>    Insert a space between the ending comma and closing bracket of a list, etc.

#### `SPACE_INSIDE_BRACKETS`

    Use spaces inside brackets, braces, and parentheses.  For example:

```python
        method_call( 1 )
        my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ]
        my_set = { 1, 2, 3 }
```

#### `SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN`

>    Set to `True` to prefer spaces around the assignment operator for default
>    or keyword arguments.

#### `SPACES_AROUND_DICT_DELIMITERS`

>    Adds a space after the opening '{' and before the ending '}' dict delimiters.

```python
        {1: 2}
```

>    will be formatted as:

```python
        { 1: 2 }
```

#### `SPACES_AROUND_LIST_DELIMITERS`

>    Adds a space after the opening '[' and before the ending ']' list delimiters.

```python
    [1, 2]
```

>    will be formatted as:

```python
    [ 1, 2 ]
```

#### `SPACES_AROUND_POWER_OPERATOR`

>    Set to `True` to prefer using spaces around `**`.

#### `SPACES_AROUND_SUBSCRIPT_COLON`

>    Use spaces around the subscript / slice operator.  For example:

```python
    my_list[1 : 10 : 2]
```

##### `SPACES_AROUND_TUPLE_DELIMITERS`

>    Adds a space after the opening '(' and before the ending ')' tuple delimiters.

```python
    (1, 2, 3)
```

>    will be formatted as:

```python
    ( 1, 2, 3 )
```

#### `SPACES_BEFORE_COMMENT`

>    The number of spaces required before a trailing comment.
>    This can be a single value (representing the number of spaces
>    before each trailing comment) or list of values (representing
>    alignment column values; trailing comments within a block will
>    be aligned to the first column value that is greater than the maximum
>    line length within the block).

> **Note:** Lists of values may need to be quoted in some contexts
> (eg. shells or editor config files).

>    For example, with `spaces_before_comment=5`:

```python
    1 + 1 # Adding values
```

>    will be formatted as:

```python
    1 + 1     # Adding values <-- 5 spaces between the end of the statement and comment
```

>    with `spaces_before_comment="15, 20"`:

```python
    1 + 1 # Adding values
    two + two # More adding

    longer_statement # This is a longer statement
    short # This is a shorter statement

    a_very_long_statement_that_extends_beyond_the_final_column # Comment
    short # This is a shorter statement
```

>    will be formatted as:

```python
    1 + 1          # Adding values <-- end of line comments in block aligned to col 15
    two + two      # More adding

    longer_statement    # This is a longer statement <-- end of line comments in block aligned to col 20
    short               # This is a shorter statement

    a_very_long_statement_that_extends_beyond_the_final_column  # Comment <-- the end of line comments are aligned based on the line length
    short                                                       # This is a shorter statement
```

#### `SPLIT_ALL_COMMA_SEPARATED_VALUES`

>    If a comma separated list (`dict`, `list`, `tuple`, or function `def`) is
>    on a line that is too long, split such that each element is on a separate
>    line.

#### `SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES`

>    Variation on `SPLIT_ALL_COMMA_SEPARATED_VALUES` in which, if a
>    subexpression with a comma fits in its starting line, then the
>    subexpression is not split. This avoids splits like the one for
>    `b` in this code:

```python
    abcdef(
        aReallyLongThing: int,
        b: [Int,
            Int])
```

>    with the new knob this is split as:

```python
    abcdef(
        aReallyLongThing: int,
        b: [Int, Int])
```

#### `SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED`

>    Split before arguments if the argument list is terminated by a comma.

#### `SPLIT_BEFORE_ARITHMETIC_OPERATOR`

>    Set to `True` to prefer splitting before `+`, `-`, `*`, `/`, `//`, or `@`
>    rather than after.

#### `SPLIT_BEFORE_BITWISE_OPERATOR`

>    Set to `True` to prefer splitting before `&`, `|` or `^` rather than after.

#### `SPLIT_BEFORE_CLOSING_BRACKET`

>    Split before the closing bracket if a `list` or `dict` literal doesn't fit
>    on a single line.

#### `SPLIT_BEFORE_DICT_SET_GENERATOR`

>    Split before a dictionary or set generator (`comp_for`). For example, note
>    the split before the `for`:

```python
    foo = {
        variable: 'Hello world, have a nice day!'
        for variable in bar if variable != 42
    }
```

#### `SPLIT_BEFORE_DOT`

>    Split before the `.` if we need to split a longer expression:

```python
    foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))
```

>    would reformat to something like:

```python
    foo = ('This is a really long string: {}, {}, {}, {}'
           .format(a, b, c, d))
```

#### `SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN`

>    Split after the opening paren which surrounds an expression if it doesn't
>    fit on a single line.

#### `SPLIT_BEFORE_FIRST_ARGUMENT`

>    If an argument / parameter list is going to be split, then split before the
>    first argument.

#### `SPLIT_BEFORE_LOGICAL_OPERATOR`

>    Set to `True` to prefer splitting before `and` or `or` rather than after.

#### `SPLIT_BEFORE_NAMED_ASSIGNS`

>    Split named assignments onto individual lines.

#### `SPLIT_COMPLEX_COMPREHENSION`

>    For list comprehensions and generator expressions with multiple clauses
>    (e.g multiple `for` calls, `if` filter expressions) and which need to be
>    reflowed, split each clause onto its own line. For example:

```python
    result = [
        a_var + b_var for a_var in xrange(1000) for b_var in xrange(1000)
        if a_var % b_var]
```

>    would reformat to something like:

```python
    result = [
        a_var + b_var
        for a_var in xrange(1000)
        for b_var in xrange(1000)
        if a_var % b_var]
```

#### `SPLIT_PENALTY_AFTER_OPENING_BRACKET`

>    The penalty for splitting right after the opening bracket.

#### `SPLIT_PENALTY_AFTER_UNARY_OPERATOR`

>    The penalty for splitting the line after a unary operator.

#### `SPLIT_PENALTY_ARITHMETIC_OPERATOR`

>    The penalty of splitting the line around the `+`, `-`, `*`, `/`, `//`, `%`,
>    and `@` operators.

#### `SPLIT_PENALTY_BEFORE_IF_EXPR`

>    The penalty for splitting right before an `if` expression.

#### `SPLIT_PENALTY_BITWISE_OPERATOR`

>    The penalty of splitting the line around the `&`, `|`, and `^` operators.

#### `SPLIT_PENALTY_COMPREHENSION`

>    The penalty for splitting a list comprehension or generator expression.

#### `SPLIT_PENALTY_EXCESS_CHARACTER`

>    The penalty for characters over the column limit.

#### `SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT`

>    The penalty incurred by adding a line split to the logical line. The more
>    line splits added the higher the penalty.

#### `SPLIT_PENALTY_IMPORT_NAMES`

>    The penalty of splitting a list of `import as` names. For example:

```python
    from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
                                                              long_argument_2,
                                                              long_argument_3)
```

>    would reformat to something like:

```python
    from a_very_long_or_indented_module_name_yada_yad import (
        long_argument_1, long_argument_2, long_argument_3)
```

#### `SPLIT_PENALTY_LOGICAL_OPERATOR`

>    The penalty of splitting the line around the `and` and `or` operators.

#### `USE_TABS`

>    Use the Tab character for indentation.

## (Potentially) Frequently Asked Questions

### Why does YAPF destroy my awesome formatting?

YAPF tries very hard to get the formatting correct. But for some code, it won't
be as good as hand-formatting. In particular, large data literals may become
horribly disfigured under YAPF.

The reasons for this are manyfold. In short, YAPF is simply a tool to help
with development. It will format things to coincide with the style guide, but
that may not equate with readability.

What can be done to alleviate this situation is to indicate regions YAPF should
ignore when reformatting something:

```python
# yapf: disable
FOO = {
    # ... some very large, complex data literal.
}

BAR = [
    # ... another large data literal.
]
# yapf: enable
```

You can also disable formatting for a single literal like this:

```python
BAZ = {
    (1, 2, 3, 4),
    (5, 6, 7, 8),
    (9, 10, 11, 12),
}  # yapf: disable
```

To preserve the nice dedented closing brackets, use the
`dedent_closing_brackets` in your style. Note that in this case all
brackets, including function definitions and calls, are going to use
that style.  This provides consistency across the formatted codebase.

### Why Not Improve Existing Tools?

We wanted to use clang-format's reformatting algorithm. It's very powerful and
designed to come up with the best formatting possible. Existing tools were
created with different goals in mind, and would require extensive modifications
to convert to using clang-format's algorithm.

### Can I Use YAPF In My Program?

Please do! YAPF was designed to be used as a library as well as a command line
tool. This means that a tool or IDE plugin is free to use YAPF.

### I still get non-PEP8 compliant code! Why?

YAPF tries very hard to be fully PEP 8 compliant. However, it is paramount
to not risk altering the semantics of your code. Thus, YAPF tries to be as
safe as possible and does not change the token stream
(e.g., by adding parentheses).
All these cases however, can be easily fixed manually. For instance,

```python
from my_package import my_function_1, my_function_2, my_function_3, my_function_4, my_function_5

FOO = my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 + my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8
```

won't be split, but you can easily get it right by just adding parentheses:

```python
from my_package import (my_function_1, my_function_2, my_function_3,
                        my_function_4, my_function_5)

FOO = (my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 +
       my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8)
```

## Gory Details

### Algorithm Design

The main data structure in YAPF is the `LogicalLine` object. It holds a list
of `FormatToken`\s, that we would want to place on a single line if there
were no column limit. An exception being a comment in the middle of an
expression statement will force the line to be formatted on more than one line.
The formatter works on one `LogicalLine` object at a time.

An `LogicalLine` typically won't affect the formatting of lines before or
after it. There is a part of the algorithm that may join two or more
`LogicalLine`\s into one line. For instance, an if-then statement with a
short body can be placed on a single line:

```python
if a == 42: continue
```

YAPF's formatting algorithm creates a weighted tree that acts as the solution
space for the algorithm. Each node in the tree represents the result of a
formatting decision --- i.e., whether to split or not to split before a token.
Each formatting decision has a cost associated with it. Therefore, the cost is
realized on the edge between two nodes. (In reality, the weighted tree doesn't
have separate edge objects, so the cost resides on the nodes themselves.)

For example, take the following Python code snippet. For the sake of this
example, assume that line (1) violates the column limit restriction and needs to
be reformatted.

```python
def xxxxxxxxxxx(aaaaaaaaaaaa, bbbbbbbbb, cccccccc, dddddddd, eeeeee):  # 1
    pass                                                               # 2
```

For line (1), the algorithm will build a tree where each node (a
`FormattingDecisionState` object) is the state of the line at that token given
the decision to split before the token or not. Note: the `FormatDecisionState`
objects are copied by value so each node in the graph is unique and a change in
one doesn't affect other nodes.

Heuristics are used to determine the costs of splitting or not splitting.
Because a node holds the state of the tree up to a token's insertion, it can
easily determine if a splitting decision will violate one of the style
requirements. For instance, the heuristic is able to apply an extra penalty to
the edge when not splitting between the previous token and the one being added.

There are some instances where we will never want to split the line, because
doing so will always be detrimental (i.e., it will require a backslash-newline,
which is very rarely desirable). For line (1), we will never want to split the
first three tokens: `def`, `xxxxxxxxxxx`, and `(`. Nor will we want to
split between the `)` and the `:` at the end. These regions are said to be
"unbreakable." This is reflected in the tree by there not being a "split"
decision (left hand branch) within the unbreakable region.

Now that we have the tree, we determine what the "best" formatting is by finding
the path through the tree with the lowest cost.

And that's it!


django-taggit
=============

.. image:: https://jazzband.co/static/img/badge.svg
   :target: https://jazzband.co/
   :alt: Jazzband

.. image:: https://img.shields.io/pypi/pyversions/django-taggit.svg
   :target: https://pypi.org/project/django-taggit/
   :alt: Supported Python versions

.. image:: https://img.shields.io/pypi/djversions/django-taggit.svg
   :target: https://pypi.org/project/django-taggit/
   :alt: Supported Django versions

.. image:: https://github.com/jazzband/django-taggit/workflows/Test/badge.svg
   :target: https://github.com/jazzband/django-taggit/actions
   :alt: GitHub Actions

.. image:: https://codecov.io/gh/jazzband/django-taggit/coverage.svg?branch=master
    :target: https://codecov.io/gh/jazzband/django-taggit?branch=master

This is a `Jazzband <https://jazzband.co>`_ project. By contributing you agree
to abide by the `Contributor Code of Conduct
<https://jazzband.co/about/conduct>`_ and follow the `guidelines
<https://jazzband.co/about/guidelines>`_.

``django-taggit`` a simpler approach to tagging with Django.  Add ``"taggit"`` to your
``INSTALLED_APPS`` then just add a TaggableManager to your model and go:

.. code:: python

    from django.db import models

    from taggit.managers import TaggableManager

    class Food(models.Model):
        # ... fields here

        tags = TaggableManager()

Then you can use the API like so:

.. code:: pycon

    >>> apple = Food.objects.create(name="apple")
    >>> apple.tags.add("red", "green", "delicious")
    >>> apple.tags.all()
    [<Tag: red>, <Tag: green>, <Tag: delicious>]
    >>> apple.tags.remove("green")
    >>> apple.tags.all()
    [<Tag: red>, <Tag: delicious>]
    >>> Food.objects.filter(tags__name__in=["red"])
    [<Food: apple>, <Food: cherry>]

Tags will show up for you automatically in forms and the admin.

``django-taggit`` requires Django 3.2 or greater.

For more info check out the `documentation
<https://django-taggit.readthedocs.io/>`_. And for questions about usage or
development you can create an issue on Github (if your question is about
usage please add the `question` tag).


Flexx
=====

[![CI](https://github.com/flexxui/flexx/workflows/CI/badge.svg)](https://github.com/flexxui/flexx/actions)
[![Documentation Status](https://readthedocs.org/projects/flexx/badge/?version=latest)](https://flexx.readthedocs.org)
[![PyPI version](https://badge.fury.io/py/flexx.svg)](https://badge.fury.io/py/flexx)

Want to stay up-to-date about (changes to) Flexx? Subscribe to the [NEWS issue](https://github.com/flexxui/flexx/issues/477).

Introduction
------------

[Flexx](https://flexx.readthedocs.io) is a pure Python toolkit for
creating graphical user interfaces (GUI's), that uses web technology
for its rendering. Apps are written purely in Python; The
[PScript](https://pscript.readthedocs.io) transpiler generates the
necessary JavaScript on the fly.

You can use Flexx to create (cross platform) desktop applications, web
applications, and export an app to a standalone HTML document. It also
works in the Jupyter notebook.

The docs are on [Readthedocs](http://flexx.readthedocs.io).
the code is on [Github](http://github.com/flexxui/flexx).

Example
-------

Click the image below for an interactive example:

[![demo](https://dl.dropboxusercontent.com/s/x4s7wgv6tpyqsqo/flexx_demo_300.png)](http://flexx.readthedocs.io/en/latest/examples/demo_src.html)

Motivation
----------

The primary motivation for Flexx is the undeniable fact that the web
(i.e. browser technology) has become an increasingly popular method for
delivering applications to users, also for (interactive) scientific
content.

The purpose of Flexx is to provide a single application framework to
create desktop applications, web apps, and (hopefully someday) mobile apps.
By making use of browser technology, the library itself can be
relatively small and pure Python, making it widely available and easy
to use.

A word of caution
-----------------

Flexx is very versatile and
[can be used in different ways](https://flexx.readthedocs.io/en/stable/guide/running.html).
It also makes it easy to mix Python that runs on the server and Python that
runs in the browser. This is a powerful feature but this also makes it easy
to create code that becomes difficult to maintain. You, the developer, must
ensure that Python and PScript code are clearly separated.

Installation
------------

Flexx requires Python 3.5+ and also works on pypy. Further,
it depends on:

* the [Tornado](http://www.tornadoweb.org) library (pure Python).
* the [PScript](http://github.com/flexxui/pscript) library (a pure Python flexxui project).
* the [Webruntime](http://github.com/flexxui/webruntime) library (a pure Python flexxui project).
* the [Dialite](http://github.com/flexxui/dialite) library (a pure Python flexxui project).

To install the latest release (and dependencies), use either of:

```bash
# Install latest release
pip install flexx

# Install latest from Github
pip install -U https://github.com/flexxui/flexx/archive/master.zip
```
Or get the bleeding edge with:

* ``pip install https://github.com/flexxui/flexx/archive/master.zip``

Supported browsers
------------------

Flexx aims to support all modern browsers, including Firefox, Chrome and Edge.
Internet Explorer version 10 and up should work, but some things may be flaky.

For running desktop apps, it is needed to have Firefox or NW.js installed.

License
-------

Flexx makes use of the liberal 2-clause BSD license. See LICENSE for details.


Subliminal
==========
Subtitles, faster than your thoughts.

.. image:: https://img.shields.io/pypi/v/subliminal.svg
    :target: https://pypi.python.org/pypi/subliminal
    :alt: Latest Version

.. image:: https://travis-ci.org/Diaoul/subliminal.svg?branch=develop
    :target: https://travis-ci.org/Diaoul/subliminal
    :alt: Travis CI build status

.. image:: https://readthedocs.org/projects/subliminal/badge/?version=latest
    :target: https://subliminal.readthedocs.org/
    :alt: Documentation Status

.. image:: https://coveralls.io/repos/Diaoul/subliminal/badge.svg?branch=develop&service=github
    :target: https://coveralls.io/github/Diaoul/subliminal?branch=develop
    :alt: Code coverage

.. image:: https://img.shields.io/github/license/Diaoul/subliminal.svg
    :target: https://github.com/Diaoul/subliminal/blob/master/LICENSE
    :alt: License

.. image:: https://img.shields.io/badge/gitter-join%20chat-1dce73.svg
    :alt: Join the chat at https://gitter.im/Diaoul/subliminal
    :target: https://gitter.im/Diaoul/subliminal

:Project page: https://github.com/Diaoul/subliminal
:Documentation: https://subliminal.readthedocs.org/

Usage
-----
CLI
^^^
Download English subtitles::

    $ subliminal download -l en The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
    Collecting videos  [####################################]  100%
    1 video collected / 0 video ignored / 0 error
    Downloading subtitles  [####################################]  100%
    Downloaded 1 subtitle

Library
^^^^^^^
Download best subtitles in French and English for videos less than two weeks old in a video folder:

.. code:: python

    #!/usr/bin/env python

    from datetime import timedelta

    from babelfish import Language
    from subliminal import download_best_subtitles, region, save_subtitles, scan_videos

    # configure the cache
    region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'})

    # scan for videos newer than 2 weeks and their existing subtitles in a folder
    videos = scan_videos('/video/folder', age=timedelta(weeks=2))

    # download best subtitles
    subtitles = download_best_subtitles(videos, {Language('eng'), Language('fra')})

    # save them to disk, next to the video
    for v in videos:
        save_subtitles(v, subtitles[v])

Docker
^^^^^^
Run subliminal in a docker container::

    $ docker run --rm --name subliminal -v subliminal_cache:/usr/src/cache -v /tvshows:/tvshows -it diaoulael/subliminal download -l en /tvshows/The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4

Installation
------------
Subliminal can be installed as a regular python module by running::

    $ [sudo] pip install subliminal

For a better isolation with your system you should use a dedicated virtualenv or install for your user only using
the ``--user`` flag.

Nautilus/Nemo integration
-------------------------
See the dedicated `project page <https://github.com/Diaoul/nautilus-subliminal>`_ for more information.


==============
djangocms-blog
==============

|Gitter| |PyPiVersion| |PyVersion| |GAStatus| |TestCoverage| |CodeClimate| |License|

django CMS blog application - Support for multilingual posts, placeholders, social network meta tags and configurable apphooks.

Supported Django versions:

* Django 3.2 - 4.2

Supported django CMS versions:

* django CMS 3.9 - 3.11+

************
Installation
************

See `installation documentation`_

********
Features
********

See `features documentation`_  for all the features details

* Support for `django-app-enabler`_ autoconfiguration.
* Placeholder content editing
* Frontend editing using django CMS frontend editor
* Multilingual support using django-parler
* Twitter cards, Open Graph and Google+ snippets meta tags
* Optional simpler TextField-based content editing
* Multisite (posts can be visible in one or more Django sites on the same project)
* Per-Apphook configuration
* Configurable permalinks
* Configurable django CMS menu
* Per-Apphook templates set
* Auto Apphook setup
* Django sitemap framework
* django CMS Wizard integration
* Desktop notifications
* Liveblog

*****************************
Known djangocms-blog websites
*****************************

See DjangoPackages for an updated list https://www.djangopackages.com/packages/p/djangocms-blog/

.. _features documentation: http://djangocms-blog.readthedocs.io/en/latest/features/
.. _installation documentation: http://djangocms-blog.readthedocs.io/en/latest/installation.html
.. _permalinks: http://djangocms-blog.readthedocs.io/en/latest/features/permalinks.html
.. _cmsplugin-filer migration documentation: http://djangocms-blog.readthedocs.io/en/latest/cmsplugin_filer.html
.. _django-app-enabler: https://github.com/nephila/django-app-enabler

.. |Gitter| image:: https://img.shields.io/badge/GITTER-join%20chat-brightgreen.svg?style=flat-square
    :target: https://gitter.im/nephila/applications
    :alt: Join the Gitter chat

.. |PyPiVersion| image:: https://img.shields.io/pypi/v/djangocms-blog.svg?style=flat-square
    :target: https://pypi.python.org/pypi/djangocms-blog
    :alt: Latest PyPI version

.. |PyVersion| image:: https://img.shields.io/pypi/pyversions/djangocms-blog.svg?style=flat-square
    :target: https://pypi.python.org/pypi/djangocms-blog
    :alt: Python versions

.. |GAStatus| image:: https://github.com/nephila/djangocms-blog/workflows/Tox%20tests/badge.svg
    :target: https://github.com/nephila/djangocms-blog
    :alt: Latest CI build status

.. |TestCoverage| image:: https://img.shields.io/coveralls/nephila/djangocms-blog/master.svg?style=flat-square
    :target: https://coveralls.io/r/nephila/djangocms-blog?branch=master
    :alt: Test coverage

.. |License| image:: https://img.shields.io/github/license/nephila/djangocms-blog.svg?style=flat-square
   :target: https://pypi.python.org/pypi/djangocms-blog/
    :alt: License

.. |CodeClimate| image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg?style=flat-square
   :target: https://codeclimate.com/github/nephila/djangocms-blog
   :alt: Code Climate


# Plex-Trakt-Sync

![Python Versions][python-versions-badge]

This project adds a two-way-sync between trakt.tv and Plex Media Server. It
requires a trakt.tv account but no Plex premium and no Trakt VIP subscriptions,
unlike the Plex app provided by Trakt.

![image](https://raw.githubusercontent.com/twolaw/PlexTraktSync/img/plextraktsync_banner.png)

Originally created by [@Taxel][@taxel], now maintained by [contributors].

[@taxel]: https://github.com/Taxel
[contributors]: https://github.com/Taxel/PlexTraktSync/graphs/contributors

Note: The PyTrakt API keys are not stored securely, so if you do not want to
have a file containing those on your harddrive, you can not use this project.

## Contribute

**Looking for a way to contribute?**

- find issues with the [help-wanted] label
- improve documentation [docs-needed] label
- you can also just [create a pull request] to improve documentation
- ... more [developer contribution](CONTRIBUTING.md) docs

[help-wanted]: https://github.com/Taxel/PlexTraktSync/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
[docs-needed]: https://github.com/Taxel/PlexTraktSync/issues?q=label%3A%22docs+needed%22+sort%3Aupdated-desc
[create a pull request]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request

---

- [Plex-Trakt-Sync](#plex-trakt-sync)
  - [Features](#features)
  - [Pre-requisites](#pre-requisites)
  - [Installation](#installation)
    - [pipx](#pipx)
    - [Docker Compose](#docker-compose)
    - [Install code from Pull request](#install-code-from-pull-request), development
    - [Windows Setup (optional alternative)](#windows-setup-optional-alternative), unsupported
    - [Unraid setup](#unraid-setup), unsupported
    - [GitHub](#github), unsupported
  - [Setup](#setup)
  - [Configuration](#configuration)
    - [Libraries](#libraries)
    - [Per server configuration](#per-server-configuration)
    - [Logging](#logging)
  - [Commands](#commands)
    - [Sync](#sync)
    - [Unmatched](#unmatched)
    - [Info command](#info-command)
    - [Inspect](#inspect)
    - [Watch](#watch)
      - [Systemd setup](#systemd-setup)
      - [Systemd user setup](#systemd-user-setup)
  - [Good practices](#good-practices)
  - [Troubleshooting](#troubleshooting)

[python-versions-badge]: https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue

## Features

- Media in Plex are added to Trakt collection
- Ratings are synced
- Watched status are synced (dates are not reported from Trakt to Plex)
- Liked lists in Trakt are downloaded and all movies in Plex belonging to that
  list are added
- Watchlists are synced
- You can edit the config file to choose what to sync
- None of the above requires a Plex Pass or Trakt VIP membership.
  Downside: Needs to be executed manually or via cronjob,
  can not use live data via webhooks.

## Pre-requisites

The script is known to work with Python 3.8-3.12 versions.

## Installation

### pipx

Installation with [pipx][install-pipx].

```
pipx install PlexTraktSync
```

or, to install specific version:

```
pipx install PlexTraktSync==0.15.2 --force
```

and to upgrade:

```
plextraktsync self-update
```

which just calls `pipx` with:

```
pipx upgrade PlexTraktSync
```

to run:

```
plextraktsync sync
```

NOTE: `pipx` install will use OS specific paths for Config, Logs, Cache, see
[platformdirs] documentation for details or check output of [info command](#info-command).

[platformdirs]: https://pypi.org/project/platformdirs
[install-pipx]: https://github.com/pypa/pipx#install-pipx

### Docker Compose

You can setup docker compose file like this:

```yaml
version: "2"
services:
  plextraktsync:
    image: ghcr.io/taxel/plextraktsync
    command: sync
    container_name: plextraktsync
    restart: on-failure:2
    volumes:
      - ./config:/app/config
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Tallinn
```

You can use specific version `0.25.16`:

- `image: ghcr.io/taxel/plextraktsync:0.25.16`

or latest 0.25.x version:

- `image: ghcr.io/taxel/plextraktsync:0.25`

Note: `main` is development version and reporting bugs against development versions are not supported.

#### Run the Docker Container

To run sync:

```
docker-compose run --rm plextraktsync sync
```

The container will stop after the sync is completed. Read Setup section to run
it automatically at set intervals.

### Install code from Pull request

This is to install development version to test if pull request would fix some problem.

See contributing guide how to [install code from pull request].

[install code from pull request]: CONTRIBUTING.md#install-code-from-pull-request

### Windows Setup (optional alternative)

NOTE: _This installation method is not supported. It's documented solely by user contribution._

- Download the latest `.zip` release from https://github.com/Taxel/PlexTraktSync/tags
- Run `setup.bat` to install requirements and create optional shortcuts and
  routines _(requires Windows 7sp1 - 11)_.

### Unraid setup

NOTE: _This installation method is not supported. It's documented solely by user contribution._

Option 1 for container creation:
Create a manual Unraid container of PlexTraktSync:

- Go to the Docker section, under "Docker Containers" and click "Add Container".
  - Click the advanced view to see all of the available parameters.
  - Leave the template blank/unselected.
  - Under Name: enter a name for the docker (e.g., PlexTraktSync).
  - Under Repository: enter `ghcr.io/taxel/plextraktsync:latest` (or whatever tag you want).
  - Under Extra Parameters: enter `-it` for interactive mode.
- Click "Apply".
- The container should start automatically. If not, start it.
- Enter the console for the container.
- Enter `plextraktsync` to start the credential process described above.

Option 2 for container creation:
Utilize the "Community Apps" Unraid Plugin.

- Go to the Plugins tab, paste the Community Apps URL in the URL area, and click "Install".

Once installed (or if already installed):

- Go to the (newly created) Apps tab and search "plextraktsync", and click on
  the App, and click "Install" (https://forums.unraid.net/topic/38582-plug-in-community-applications/)
- Take all the default settings (the -it switch as outlined elsewhere in the
  README is already present), and click "Apply".
- The container then installs, and will start.

Schedule (cron) the container to start at given intervals to process the sync

- Go to the Plugins tab, past the User Scripts URL in the URL area, and click "Install" (https://forums.unraid.net/topic/48286-plugin-ca-user-scripts/)

Once installed (or if already installed):

- Go to the Plugins tab, click on "User Scripts", and click the "Add New Script" button
- Name your script accordingly
- Click the "gear" icon next to the script name, and click "Edit Script"
- Below the "#!/bin/bash" line add: `docker start PlexTraktSync`
- Click "Save Changes"
- Set the schedule accordingly using the dropdown menu next to the "Run in Background" button

### GitHub

NOTE: _This installation method is not supported._ You will not get support if you use this installation method.

Installing from GitHub is considered developer mode, and it's documented in
[CONTRIBUTING.md](CONTRIBUTING.md#checking-out-code).

## Setup

- You will need to create a Trakt API app if you do not already have one:

  - Visit https://trakt.tv/oauth/applications/new
  - Give it a meaningful name
  - Enter `urn:ietf:wg:oauth:2.0:oob` as the redirect url
  - You can leave Javascript origins and the Permissions checkboxes blank

- Run `plextraktsync login`, the script will ask for missing credentials

  > **Note**
  > To setup the credentials in the Docker Container, refer to the [Run the Docker Container](#run-the-docker-container) section

- At first run you will be asked to setup Trakt and Plex access.

  Follow the instructions, your credentials and API keys will be stored in
  `.env`and `.pytrakt.json` files. Plex URL and token is stored in `servers.yml`.

  If you have [2 Factor Authentication enabled on Plex][two-factor-authentication], input the code when prompted. If you don't have 2FA enabled, just leave the prompt blank and press Enter.

[two-factor-authentication]: https://support.plex.tv/articles/two-factor-authentication/#toc-1:~:text=Old%20Third%2DParty%20Apps%20%26%20Tools

- Cronjobs can be optionally used on Linux or macOS to run the script at set intervals.

  For example, to run this script in a cronjob every two hours:

  ```
  $ crontab -e
  0 */2 * * * $HOME/.local/bin/plextraktsync sync
  ```

  - Note the command in the example above may not immediately work. Use the `which plextraktsync` command to locate your system's plextraktsync executable file and update it accordingly.

- Instead of cron, a docker scheduler like [Ofelia][ofelia] can also be used to
  run the script at set intervals.

[ofelia]: https://github.com/mcuadros/ofelia/

A docker-compose example with a 6h interval:

```yaml
version: "2"
services:
  scheduler:
    image: mcuadros/ofelia:latest
    container_name: scheduler
    depends_on:
      - plextraktsync
    command: daemon --docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      ofelia.job-run.plextraktsync.schedule: "@every 6h"
      ofelia.job-run.plextraktsync.container: "plextraktsync"
  plextraktsync:
    image: ghcr.io/taxel/plextraktsync:latest
    container_name: plextraktsync
    command: sync
    volumes:
      - ./config:/app/config
```

## Configuration

To disable parts of the functionality of this software, look no further than
`config.yml`. At first run, the script will create `config.yml` based on
`config.default.yml`. If you want to customize settings before first run (eg.
you don't want full sync) you can copy and edit `config.yml` before launching
the script. Here, in the sync section, you can disable the following things
by setting them from `true` to `false` in a text editor:

- Downloading liked lists from Trakt and adding them to Plex
- Syncing the watchlists between Plex and Trakt
- Syncing the watched status between Plex and Trakt
- Syncing the collected status between Plex and Trakt

The first execution of the script will (depending on your PMS library size)
take a long time. After that, movie details and Trakt lists are cached, so
it should run a lot quicker the second time. This does mean, however, that
Trakt lists are not updated dynamically (which is fine for lists like "2018
Academy Award Nominees" but might not be ideal for lists that are updated
often). Here are the execution times on my Plex server: First run - 1228
seconds, second run - 111 seconds

You can view sync progress in the `plextraktsync.log` file which will be
created.

You can use `--edit` or `--locate` flags to `config` command to open config
file in editor or in file browser.

### Libraries

By default, all libraries are processed. You can disable libraries by name by
changing `excluded-libraries` in `config.yml`.

You can also set `excluded-libraries` per server in `servers.yml`:

```yml
servers:
  Example1:
    token: ~
    urls:
      - http://localhost:32400
    config:
      excluded-libraries:
        - "Family Movies"
```

Additionally, you can list only libraries to be processed, in this case global
`excluded-libraries` will not be used for this server.

```yml
servers:
  Example1:
    token: ~
    urls:
      - http://localhost:32400
    config:
      libraries:
        - "Movies"
        - "TV Shows"
```

you can see the final list of libraries with info command:

```commandline
$ plextraktsync --server=Example1 info
Enabled 2 libraries in Plex Server:
 - 1: Movies
 - 2: TV Shows
```

### Per server configuration

If you want to specify your config per server you can do so inside of
`servers.yml`. Within the `config` part of the server configuration you can
specify how that specific server should work.

```yml
servers:
  Example1:
    token: ~
    urls:
      - http://localhost:32400
    config:
      sync:
        plex_to_trakt:
          collection: true
        trakt_to_plex:
          liked_lists: false
```

Using `sync` in a server config overrides the global sync-config in
`config.yml`.

This can also be used to have different configs between different libraries. To
be able to do this you specify the number of servers you need (most likely
equal to the number of different config setups you need). For example:

```yml
servers:
  Example1:
    token: ~
    urls:
      - http://localhost:32400
    config:
      libraries:
        - "Movies"
      sync:
        plex_to_trakt:
          ratings: true
          watched_status: true
        trakt_to_plex:
          ratings: true
          watched_status: true
  Example2:
    token: ~
    urls:
      - http://localhost:32400
    config:
      libraries:
        - "TV Shows"
      sync:
        plex_to_trakt:
          ratings: true
          watched_status: false
        trakt_to_plex:
          ratings: true
          watched_status: false
```

The above config would make it so that the "Movies" library syncs both ratings
and watched status, while the "TV Shows" library only syncs ratings. To then
run the sync you need to specify `--server Example1` or `--server Example2` to
run the sync for that specific server.

Running the sync command without `--server` will use default server from `.env`

If you want to run these jobs using `ofelia`, you can do so by running
something similar to this in your `docker-compose.yml`:

```yml
services:
  plextraktsync:
    image: ghcr.io/taxel/plextraktsync
    command: sync
    container_name: plextraktsync
    profiles: ["schedule"]
    volumes:
      - /configs/mediarr/plextraktsync:/app/config
    environment:
      - PUID=1000
      - PGID=1000
    depends_on:
      - plex
  scheduler:
    image: mcuadros/ofelia:latest
    container_name: scheduler
    command: daemon --docker
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      ofelia.job-run.plextraktsync.schedule: "0 6,18 * * *"
      ofelia.job-run.plextraktsync.container: "plextraktsync"
      ofelia.job-run.plextraktsync.command: "--server 'Example1' sync"
      ofelia.job-run.plextraktsync2.schedule: "0 12,0 * * *"
      ofelia.job-run.plextraktsync2.container: "plextraktsync"
      ofelia.job-run.plextraktsync2.command: "--server 'Example2' sync"
```

If you are running only one PlexTraktSync container, you need to make sure that
the two jobs Ofelia jobs don't run at the same time. Ofelia skips scheduling a
new job run if the previous job is still running.

Depending on how long it takes for the job to run on your server you might have
to keep the schedule of the two jobs seperated with a few minutes or a few
hours. If you have two different PlexTraktSync containers in your docker
compose, you can run them at the same time.

The above config means that a job is running every 6 hours, alternating between
the two "servers". The PlexTraktSync container also has a [docker compose
profile] called "schedule" which means that it won't run automatically when you
run for example `docker-compose up`.

[docker compose profile]: https://docs.docker.com/compose/profiles/

### Logging

The logging level by default is `INFO`. This can be changed to DEBUG by editing
the "debug" variable in `config.yml` to `true`.

By default the logs will append, if you wish to maintain the log of only your
last run then edit the "append" variable in `config.yml` to `false`.

## Commands

Run `plextraktsync --help` to see available commands.
Run `plextraktsync COMMAND --help` to see help for `COMMAND`.

```
$ plextraktsync --help
Usage: plextraktsync [OPTIONS] COMMAND [ARGS]...

  Plex-Trakt-Sync is a two-way-sync between trakt.tv and Plex Media Server

Options:
  --version              Print version and exit
  --no-cache             Disable cache in for Trakt HTTP requests
  --no-progressbar       Disable progressbar
  --batch-delay INTEGER  Time in seconds between each collection batch submit
                         to Trakt  [default: 5]
  --server NAME          Plex Server name from servers.yml
  --help                 Show this message and exit.

Commands:
  bug-report         Create a pre-populated GitHub issue with information...
  cache              Manage and analyze Requests Cache.
  clear-collections  Clear Movies and Shows collections in Trakt
  config             Print user config for debugging and bug reports.
  download           Downloads movie or subtitles to a local directory
  imdb-import        Import IMDB ratings from CSV file.
  info               Print application and environment version info
  inspect            Inspect details of an object
  login              Log in to Plex and Trakt if needed
  plex-login         Log in to Plex Account to obtain Access Token.
  self-update        Update PlexTraktSync to the latest version using pipx
  sync               Perform sync between Plex and Trakt
  trakt-login        Log in to Trakt Account to obtain Access Token.
  unmatched          List media that has no match in Trakt or Plex
  watch              Listen to events from Plex
  watched-shows      Print a table of watched shows
```

You can [contribute](#contribute) yourself missing documentation.

### Sync

The `sync` subcommand supports `--sync=shows` and `--sync=movies` options,
so you can sync only specific library types.
Or only watchlist: `--sync=watchlist`.

```
➔ plextraktsync sync --help
Usage: plextraktsync sync [OPTIONS]

  Perform sync between Plex and Trakt

Options:
  --sync [all|movies|shows|watchlist]
                                  Specify what to sync  [default: all]
  --help                          Show this message and exit.
```

### Unmatched

You can use `unmatched` command to scan your library and display unmatched
movies.

Support for unmatched shows is not yet implemented.

`plextraktsync unmatched`

### Info command

The info command can be used to print package versions,
account information,
locations of Cache, Config and Logs directories

```
$ plextraktsync info
PlexTraktSync Version: 0.16.0
Python Version: 3.10.0 (default, Oct  6 2021, 01:11:32) [Clang 13.0.0 (clang-1300.0.29.3)]
Plex API Version: 4.7.2
Trakt API Version: 3.2.1
Cache Dir: /Users/glen/Library/Caches/PlexTraktSync
Config Dir: /Users/glen/Library/Application Support/PlexTraktSync
Log Dir: /Users/glen/Library/Logs/PlexTraktSync
Plex username: nobody
Trakt username: nobody
Plex Server version: 1.24.3.5033-757abe6b4, updated at: 2021-02-21 17:00:00
Server has 2 libraries: ['Movies', 'TV Shows']
```

### Inspect

Inspect command is used to get info about Plex Media Server items,
which is useful when debugging problems and reporting issues.

- Plex Web URL from your server:
  - https://app.plex.tv/desktop/#!/server/53aff62c4bb6027c1ada814d417e83ccdf4d5045/details?key=/library/metadata/123
- Plex Discover URL:
  - https://app.plex.tv/desktop/#!/provider/tv.plex.provider.discover/details?key=/library/metadata/5d7768258718ba001e311845
- Id from from your Plex Media Server:
  - `123`

```
plextraktsync inspect 123
plextraktsync inspect "https://app.plex.tv/desktop/#!/server/53aff62c4bb6027c1ada814d417e83ccdf4d5045/details?key=/library/metadata/123"
```

To avoid problems with various shells, put the value in double quotes.

### Watch

You can use the `watch` command to listen to events from Plex Media Server
and scrobble plays.

> What is scrobbling?
>
> _Scrobbling simply means automatically tracking what you’re watching. Instead
> of checking in from your phone of the website, this command runs in the
> background and automatically scrobbles back to Trakt while you enjoy watching
> your media_ - [Plex Scrobbler@blog.trakt.tv][plex-scrobbler]

[plex-scrobbler]: https://blog.trakt.tv/plex-scrobbler-52db9b016ead

To restrict scrobbling to your user **only** (recommended), set the following
in your `config.yml`:

```yaml
watch:
  username_filter: true
```

To run `watch` command:

`plextraktsync watch`

or

```
docker-compose run --rm plextraktsync watch
```

or add `command: watch` to docker compose file, and `docker-compose up -d
plextraktsync` to start the container detached:

```yaml
version: "2"
services:
  plextraktsync:
    image: ghcr.io/taxel/plextraktsync
    volumes:
      - ./config:/app/config
    command: watch
```

#### Systemd setup

Create a systemd unit so that it scrobbles automatically in the background:

```ini
[Unit]
Description=PlexTraktSync watch daemon
After=network-online.target

[Service]
ExecStart=plextraktsync watch
Restart=on-failure
RestartSec=10
User=user
Group=user

[Install]
WantedBy=multi-user.target
```

Note, depending on your install method you may need to set your ExecStart
command as follows:

```ini
ExecStart=/path/to/plextraktsync/plextraktsync.sh watch
```

Following that you will need to enable the service:

```
sudo systemctl daemon-reload
sudo systemctl start PlexTraktSync.service
sudo systemctl enable PlexTraktSync.service
```

#### Systemd user setup

You can also run as systemd user service.

This walkthough allows to use different servers with the same configuration.

This assumes `plextraktsync` is installed with `pipx` for your user.

```ini
# plextraktsync@.service
[Unit]
Description=PlexTraktSync watch daemon
After=network-online.target

[Service]
ExecSearchPath=%h/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=plextraktsync watch --server=%i
Restart=on-failure
RestartSec=10

[Install]
WantedBy=default.target
```

Install the service template file:

1. as `root`: `/etc/xdg/systemd/user/plextraktsync@.service` for all users
1. as your user: `~/.config/systemd/user/plextraktsync@.service` for your user only

Next, you need to reload systemd:

1. if installed as `root`: `sudo systemctl daemon-reload`
2. if installed as user: `systemctl --user daemon-reload`

Now create instances based on server names from `servers.yml`, in this example `SERVER_NAME`.

1. `systemctl --user start "plextraktsync@SERVER_NAME.service"`
1. `systemctl --user status "plextraktsync@SERVER_NAME.service"`

for complete logs, you can use `journalctl` (add `-f` to follow logs):

1. `journalctl --user -u "plextraktsync@SERVER_NAME.service"`

If all works, enable it for auto-start on host reboot

1. `systemctl --user enable "plextraktsync@SERVER_NAME.service"`

For systemd --user session to start without having to log in you need to enable [systemd-linger]:

1. `loginctl enable-linger`

[systemd-linger]: https://wiki.archlinux.org/title/systemd/User#Automatic_start-up_of_systemd_user_instances

## Good practices

- Using default `Plex Movie` and `Plex TV Series` [metadata agents] improves
  script compatibility (for matching or for watchlist).
  It is recommended to [migrate to the new Plex TV Series agent][migrating-agent-scanner].
- Organize your shows folders and naming according to [Plex standard][naming-tv-show-files]
  and [theMovieDatabase][tmdb] (tmdb) order. If Plex doesn't properly identify your media,
  you can use the [Fix Match][fix-match] and the [Match Hinting][plexmatch].
  Also check the Episode Ordering preference (under Advanced) to correspond with your files.
- Use tmdb as source for TV Shows if possible,
  because it's the Trakt [primary data source][tv-show-metadata]
  ([switched from tvdb in Jan-2021][tmdb-transition]).

[fix-match]: https://support.plex.tv/articles/201018497-fix-match-match/
[metadata agents]: https://support.plex.tv/articles/200241558-agents/
[migrating-agent-scanner]: https://support.plex.tv/articles/migrating-a-tv-library-to-use-the-new-plex-tv-series-agent-scanner/
[naming-tv-show-files]: https://support.plex.tv/articles/naming-and-organizing-your-tv-show-files/
[plexmatch]: https://support.plex.tv/articles/plexmatch/
[tmdb-transition]: https://blog.trakt.tv/tmdb-transition-ef3d19a5cf24
[tmdb]: https://themoviedb.org/
[tv-show-metadata]: https://blog.trakt.tv/tv-show-metadata-e6e64ed4e6ef

## Troubleshooting

### I have duplicate watched episodes sent to Trakt at every sync

Check your Plex episodes ordering compared to Trakt ordering.
If episodes are in a different order, it should not be a problem because they
are identified with ids.
But if a season or an episode is missing on Trakt (and tmdb) it can't be synced.
You can fix it by [adding the missing episodes] or edit metadata (eg. missing
tvdb or imdb ids) on [tmdb] or [report a metadata issue on Trakt][how-to-report-metadata-issues] ([answers][reports]). It's free for anyone
to sign up and edit info at tmdb. Trakt will [update from tmdb][trakt-tvshow-update] data.

[adding the missing episodes]: https://support.trakt.tv/support/solutions/articles/70000264977
[tmdb]: https://themoviedb.org/
[trakt-tvshow-update]: https://support.trakt.tv/support/solutions/articles/70000260936-how-does-movie-tv-show-information-metadata-get-updated-how-can-i-refresh-or-sync-trakt-to-tmdb-
[how-to-report-metadata-issues]: https://support.trakt.tv/support/solutions/articles/70000627644-how-to-report-metadata-issues
[reports]: https://trakt.tv/settings/reports

### I have many matching errors in logs

Make sure you use [good practices](#good-practices) about Plex agent and files
organization as stated above.
Check if episodes are not missing on Trakt as explained in previous answer, and
check if [external ids][house-of-the-dragon] are populated on tmdb.

[house-of-the-dragon]: https://www.themoviedb.org/tv/94997-house-of-the-dragon/season/1/episode/1/edit?active_nav_item=external_ids

### I have season 0 matching errors

Season 0 folder must only contain episodes belonging to season 0, also named specials.
Trailers, deleted scenes, featurettes, interviews,... must be stored in a
separate [Extra folder][extra-folder] (not in season 0) according to Plex rules.
Keep in mind that seasons 0 aren't really official so datasources (tmdb, imdb
and tvdb) sometimes don't correspond. Check season 0 of shows on trakt.tv to identify those special episodes.
Use tmdb as Plex source as much as you can.

[extra-folder]: https://support.plex.tv/articles/local-files-for-tv-show-trailers-and-extras/

### How to sync multiple users ?

The easiest way is to use containers with custom config folder for each user:
[Multi-User docker-compose][discussions/997].

[discussions/997]: https://github.com/Taxel/PlexTraktSync/discussions/997

### Can this run on Synology, HomeAssistant, Portainer,... ?

Yes using docker, check [Discussions][discussions] page.

### I have another question

Check [Discussions][discussions], maybe someone already asked and found the answer.

[discussions]: https://github.com/Taxel/PlexTraktSync/discussions


[<img src="http://gpkit.rtfd.org/en/latest/_images/gplogo.png" width=110 alt="GPkit" />](http://gpkit.readthedocs.org/)

**[Documentation](http://gpkit.readthedocs.org/)** | [Install instructions](http://gpkit.readthedocs.org/en/latest/installation.html) | [Examples](http://gpkit.readthedocs.org/en/latest/examples.html) | [Glossary](https://gpkit.readthedocs.io/en/latest/autodoc/gpkit.html) | [Citing GPkit](http://gpkit.readthedocs.org/en/latest/citinggpkit.html)

GPkit is a Python package for defining and manipulating
geometric programming models,
abstracting away the backend solver.
Supported solvers are
[mosek](http://mosek.com)
and [cvxopt](http://cvxopt.org/).

[![Build Status](https://acdl.mit.edu/csi/buildStatus/icon?job=CE_gpkit_Push_unit_tests)](https://acdl.mit.edu/csi/view/convex%20engineering/job/CE_gpkit_Push_unit_tests/) Unit tests

[![Build Status](https://acdl.mit.edu/csi/buildStatus/icon?job=CE_gpkit_Install)](https://acdl.mit.edu/csi/view/convex%20engineering/job/CE_gpkit_Install/) pip install

[![Build Status](https://acdl.mit.edu/csi/buildStatus/icon?job=CE_gpkit_Push_dependency_tests)](https://acdl.mit.edu/csi/view/convex%20engineering/job/CE_gpkit_Push_dependency_tests/) Dependencies


----------

## Table of Contents
   * [What is PEM?](http://physics.oregonstate.edu/~hetheriw/energy/topics/doc/electrochemistry/fc/basic/The_Polymer_Electrolyte_Fuel_Cell.htm)
   * [Overview](https://github.com/ECSIM/opem#overview)
   * [Installation](https://github.com/ECSIM/opem/blob/master/INSTALL.md)
   * [Usage](https://github.com/ECSIM/opem#usage)
   		* [Executable](https://github.com/ECSIM/opem#executable)
   		* [Library](https://github.com/ECSIM/opem#library)
   		* [Telegram Bot](https://github.com/ECSIM/opem#telegram-bot)
   		* [Try OPEM in Your Browser!](https://github.com/ECSIM/opem#try-opem-in-your-browser)
   		* [MATLAB](https://github.com/ECSIM/opem/tree/master/MATLAB)
   * [Issues & Bug Reports](https://github.com/ECSIM/opem#issues--bug-reports)
   * [Contribution](https://github.com/ECSIM/opem/blob/master/.github/CONTRIBUTING.md)
   * [Outputs](https://github.com/ECSIM/opem#outputs)
   * [Thanks](https://github.com/ECSIM/opem#thanks)
   * [Reference](https://github.com/ECSIM/opem#reference)
   * [Cite](https://github.com/ECSIM/opem#cite)
   * [Authors](https://github.com/ECSIM/opem/blob/master/AUTHORS.md)
   * [License](https://github.com/ECSIM/opem/blob/master/LICENSE)
   * [Show Your Support](https://github.com/ECSIM/opem#show-your-support)
   * [Changelog](https://github.com/ECSIM/opem/blob/master/CHANGELOG.md)
   * [Code of Conduct](https://github.com/ECSIM/opem/blob/master/.github/CODE_OF_CONDUCT.md)

## Overview

Modeling and simulation of proton-exchange membrane fuel cells (PEMFC) may work as a powerful tool in the research & development of renewable energy sources. The Open-Source PEMFC Simulation Tool (OPEM) is a modeling tool for evaluating the performance of proton exchange membrane fuel cells. This package is a combination of models (static/dynamic) that predict the optimum operating parameters of PEMFC. OPEM contained generic models that will accept as input, not only values of the operating variables such as anode and cathode feed gas, pressure and compositions, cell temperature and current density, but also cell parameters including the active area and membrane thickness. In addition, some of the different models of PEMFC that have been proposed in the OPEM, just focus on one particular FC stack, and some others take into account a part or all auxiliaries such as reformers. OPEM is a platform for collaborative development of PEMFC models.

Fig1. OPEM Block Diagram

		Open Hub

		PyPI Counter

		Github Stars

		Branch
		master
		develop

		CI

		Code Quality

## Usage

### Executable
- Open `CMD` (Windows) or `Terminal` (UNIX)
- Run `opem` or `python -m opem` (or run `OPEM.exe`)
- Enter PEM cell parameters (or run standard test vectors)
	1. Amphlett Static Model

				Input
				Description
				Unit

				T
				Cell operation temperature
				K

				PH2
				Partial pressure
				atm

				PO2
				Partial pressure
				atm

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

				A
				Active area
				cm^2

				l
				Membrane thickness
				cm

				lambda
				An adjustable parameter with a min value of 14 and max value of 23
				--

				R(*Optional)
				R-Electronic
				ohm

				JMax
				Maximum current density
				A/(cm^2)

				N
				Number of single cells
				--

		* For more information about this model visit here
	2. Larminie-Dicks Static Model

				Input
				Description
				Unit

				E0
				Fuel cell reversible no loss voltage
				V

				A
				The slope of the Tafel line
				V

				T
				Cell operation temperature
				K

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

				i_n
				Internal current
				A

				i_0
				Exchange current at which the overvoltage begins to move from zero
				A

				i_L
				Limiting current
				A

				RM
				The membrane and contact resistances
				ohm

				N
				Number of single cells
				--

		* For more information about this model visit here
	3. Chamberline-Kim Static Model

				Input
				Description
				Unit

				E0
				Open circuit voltage
				V

				b
				Tafel's parameter for the oxygen reduction
				V

				R
				Resistance
				ohm.cm^2

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

				A
				Active area
				cm^2

				m
				Diffusion's parameters
				V

				n
				Diffusion's parameters
				(A^-1)(cm^2)

				N
				Number of single cells
				--

		* For more information about this model visit here
	4. Padulles Dynamic Model I

				Input
				Description
				Unit

				E0
				No load voltage
				V

				T
				Fuel cell temperature
				K

				KH2
				Hydrogen valve constant
				kmol.s^(-1).atm^(-1)

				KO2
				Oxygen valve constant
				kmol.s^(-1).atm^(-1)

				tH2
				Hydrogen time constant
				s

				tO2
				Oxygen time constant
				s

				B
				Activation voltage constant
				V

				C
				Activation constant parameter
				A^(-1)

				Rint
				Fuel cell internal resistance
				ohm

				rho
				Hydrogen-Oxygen flow ratio
				--

				qH2
				Molar flow of hydrogen
				kmol/s

				N0
				Number of cells
				--

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

		* For more information about this model visit here

	5. Padulles Dynamic Model II

				Input
				Description
				Unit

				E0
				No load voltage
				V

				T
				Fuel cell temperature
				K

				KH2
				Hydrogen valve constant
				kmol.s^(-1).atm^(-1)

				KH2O
				Water valve constant
				kmol.s^(-1).atm^(-1)

				KO2
				Oxygen valve constant
				kmol.s^(-1).atm^(-1)

				tH2
				Hydrogen time constant
				s

				tH2O
				Water time constant
				s

				tO2
				Oxygen time constant
				s

				B
				Activation voltage constant
				V

				C
				Activation constant parameter
				A^(-1)

				Rint
				Fuel cell internal resistance
				ohm

				rho
				Hydrogen-Oxygen flow ratio
				--

				qH2
				Molar flow of hydrogen
				kmol/s

				N0
				Number of cells
				--

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

		* For more information about this model visit here
	6. Padulles-Hauer Dynamic Model

				Input
				Description
				Unit

				E0
				No load voltage
				V

				T
				Fuel cell temperature
				K

				KH2
				Hydrogen valve constant
				kmol.s^(-1).atm^(-1)

				KH2O
				Water valve constant
				kmol.s^(-1).atm^(-1)

				KO2
				Oxygen valve constant
				kmol.s^(-1).atm^(-1)

				tH2
				Hydrogen time constant
				s

				tH2O
				Water time constant
				s

				tO2
				Oxygen time constant
				s

				t1
				Reformer time constant
				s

				t2
				Reformer time constant
				s

				B
				Activation voltage constant
				V

				C
				Activation constant parameter
				A^(-1)

				CV
				Conversion factor
				--

				Rint
				Fuel cell internal resistance
				ohm

				rho
				Hydrogen-Oxygen flow ratio
				--

				qMethanol
				Molar flow of methanol
				kmol/s

				N0
				Number of cells
				--

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

		* For more information about this model visit here
	7. Padulles-Amphlett Dynamic Model

				Input
				Description
				Unit

				E0
				No load voltage
				V

				T
				Fuel cell temperature
				K

				KH2
				Hydrogen valve constant
				kmol.s^(-1).atm^(-1)

				KH2O
				Water valve constant
				kmol.s^(-1).atm^(-1)

				KO2
				Oxygen valve constant
				kmol.s^(-1).atm^(-1)

				tH2
				Hydrogen time constant
				s

				tH2O
				Water time constant
				s

				tO2
				Oxygen time constant
				s

				t1
				Reformer time constant
				s

				t2
				Reformer time constant
				s

				A
				Active area
				cm^2

				l
				Membrane thickness
				cm

				lambda
				An adjustable parameter with a min value of 14 and max value of 23
				--

				R(*Optional)
				R-Electronic
				ohm

				JMax
				Maximum current density
				A/(cm^2)

				CV
				Conversion factor
				--

				rho
				Hydrogen-Oxygen flow ratio
				--

				qMethanol
				Molar flow of methanol
				kmol/s

				N0
				Number of cells
				--

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

		* For more information about this model visit here
	8. Chakraborty Dynamic Model

				Input
				Description
				Unit

				E0
				No load voltage
				V

				T
				Cell operation temperature
				K

				KH2
				Hydrogen valve constant
				kmol.s^(-1).atm^(-1)

				KH2O
				Water valve constant
				kmol.s^(-1).atm^(-1)

				KO2
				Oxygen valve constant
				kmol.s^(-1).atm^(-1)

				rho
				Hydrogen-Oxygen flow ratio
				--

				Rint
				Fuel cell internal resistance
				ohm

				N0
				Number of cells
				--

				u
				Fuel utilization ratio
				--

				i-start
				Cell operating current start point
				A

				i-step
				Cell operating current step
				A

				i-stop
				Cell operating current end point
				A

		* For more information about this model visit here

	- Find your reports in `Model_Name` folder

	#### Screen Record

Screen Record

### Library

1. Amphlett Static Model
	```pycon
	>>> from opem.Static.Amphlett import Static_Analysis
	>>> Test_Vector={"T": 343.15,"PH2": 1,"PO2": 1,"i-start": 0,"i-stop": 75,"i-step": 0.1,"A": 50.6,"l": 0.0178,"lambda": 23,"N": 1,"R": 0,"JMax": 1.5,"Name": "Amphlett_Test"}
	>>> data=Static_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	 ```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				Eta_Active
				Eta activation
				List

				Eta_Conc
				Eta concentration
				List

				Eta_Ohmic
				Eta ohmic
				List

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here
2. Larminie-Dicks Static Model
	```pycon
	>>> from opem.Static.Larminie_Dicks import Static_Analysis
	>>> Test_Vector = {"A": 0.06,"E0": 1.178,"T": 328.15,"RM": 0.0018,"i_0": 0.00654,"i_L": 100.0,"i_n": 0.23,"N": 23,"i-start": 0.1,"i-stop": 98,"i-step": 0.1,"Name": "Larminiee_Test"}
	>>> data=Static_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	 ```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here
3. Chamberline-Kim Static Model
	```pycon
	>>> from opem.Static.Chamberline_Kim import Static_Analysis
	>>> Test_Vector = {"A": 50.0,"E0": 0.982,"b": 0.0689,"R": 0.328,"m": 0.000125,"n": 9.45,"N": 1,"i-start": 1,"i-stop": 42.5,"i-step": 0.1,"Name": "Chamberline_Test"}
	>>> data=Static_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here
4. Padulles Dynamic Model I
	```pycon
	>>> from opem.Dynamic.Padulles1 import Dynamic_Analysis
	>>> Test_Vector = {"T": 343,"E0": 0.6,"N0": 88,"KO2": 0.0000211,"KH2": 0.0000422,"tH2": 3.37,"tO2": 6.74,"B": 0.04777,"C": 0.0136,"Rint": 0.00303,"rho": 1.168,"qH2": 0.0004,"i-start": 0,"i-stop": 100,"i-step": 0.1,"Name": "PadullesI_Test"}
	>>> data=Dynamic_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				PO2
				Partial pressure
				List

				PH2
				Partial pressure
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here
5. Padulles Dynamic Model II
	```pycon
	>>> from opem.Dynamic.Padulles2 import Dynamic_Analysis
	>>> Test_Vector = {"T": 343,"E0": 0.6,"N0": 5,"KO2": 0.0000211,"KH2": 0.0000422,"KH2O": 0.000007716,"tH2": 3.37,"tO2": 6.74,"tH2O": 18.418,"B": 0.04777,"C": 0.0136,"Rint": 0.00303,"rho": 1.168,"qH2": 0.0004,"i-start": 0.1,"i-stop": 100,"i-step": 0.1,"Name": "Padulles2_Test"}
	>>> data=Dynamic_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				PO2
				Partial pressure
				List

				PH2
				Partial pressure
				List

				PH2O
				Partial pressure
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here
6. Padulles-Hauer Dynamic Model
	```pycon
	>>> from opem.Dynamic.Padulles_Hauer import Dynamic_Analysis
	>>> Test_Vector = {"T": 343,"E0": 0.6,"N0": 5,"KO2": 0.0000211,"KH2": 0.0000422,"KH2O": 0.000007716,"tH2": 3.37,"tO2": 6.74,"t1": 2,"t2": 2,"tH2O": 18.418,"B": 0.04777,"C": 0.0136,"Rint": 0.00303,"rho": 1.168,"qMethanol": 0.0002,"CV": 2,"i-start": 0.1,"i-stop": 100,"i-step": 0.1,"Name": "Padulles_Hauer_Test"}
	>>> data=Dynamic_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				PO2
				Partial pressure
				List

				PH2
				Partial pressure
				List

				PH2O
				Partial pressure
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here
7. Padulles-Amphlett Dynamic Model
	```pycon
	>>> from opem.Dynamic.Padulles_Amphlett import Dynamic_Analysis
	>>> Test_Vector = {"A": 50.6,"l": 0.0178,"lambda": 23,"JMax": 1.5,"T": 343,"N0": 5,"KO2": 0.0000211,"KH2": 0.0000422,"KH2O": 0.000007716,"tH2": 3.37,"tO2": 6.74,"t1": 2,"t2": 2,"tH2O": 18.418,"rho": 1.168,"qMethanol": 0.0002,"CV": 2,"i-start": 0.1,"i-stop": 75,"i-step": 0.1,"Name": "Padulles_Amphlett_Test"}
	>>> data=Dynamic_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				PO2
				Partial pressure
				List

				PH2
				Partial pressure
				List

				PH2O
				Partial pressure
				List

				Ph
				Thermal power
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				Eta_Active
				Eta activation
				List

				Eta_Conc
				Eta concentration
				List

				Eta_Ohmic
				Eta ohmic
				List

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here

8. Chakraborty Dynamic Model
	```pycon
	>>> from opem.Dynamic.Chakraborty import Dynamic_Analysis
	>>> Test_Vector = {"T": 1273,"E0": 0.6,"u":0.8,"N0": 1,"R": 3.28125 * 10**(-3),"KH2O": 0.000281,"KH2": 0.000843,"KO2": 0.00252,"rho": 1.145,"i-start": 0.1,"i-stop": 300,"i-step": 0.1,"Name": "Chakraborty_Test"}
	>>> data=Dynamic_Analysis(InputMethod=Test_Vector,TestMode=True,PrintMode=False,ReportMode=False)
	```

				Key
				Description
				Type

				Status
				Simulation status
				Bool

				P
				Power
				List

				I
				Cell operating current
				List

				V
				FC voltage
				List

				EFF
				Efficiency
				List

				PO2
				Partial pressure
				List

				PH2
				Partial pressure
				List

				PH2O
				Partial pressure
				List

				Ph
				Thermal power
				List

				Nernst Gain
				Nernst Gain
				List

				Ohmic Loss
				Ohmic Loss
				List

				V0
				Linear-Apx intercept
				Float

				K
				Linear-Apx slope
				Float

				VE
				Estimated FC voltage
				List

	- For more information about this model visit here

	#### Parameters

	1. `TestMode` : Active test mode and get/return data as `dict`, (Default : `False`)
	2. `ReportMode` : Generate reports(`.csv`,`.opem`,`.html`) and print result in console, (Default : `True`)
	3. `PrintMode` : Control printing in console, (Default : `True`)
	4. `Folder` : Reports folder, (Default : `os.getcwd()`)

	#### Note

	- Return type : `dict`

### Telegram Bot
- Send `/start` command to [OPEM BOT](https://t.me/opembot)
- Choose models from menu
- Send your test vector according to the template
- Download your results

### Try OPEM in Your Browser!
OPEM can be used online in interactive Jupyter Notebooks via the Binder service! Try it out now! :

[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ECSIM/opem/master)

- Check `.ipynb` files in `Documents` folder
- Edit and execute each part of the notes, step by step from the top panel by run button
- For executing a complete simulation, you can edit `Test_Vector` in `Full Run` section

## Issues & Bug Reports

Just fill an issue and describe it. We'll check it ASAP!
or send an email to [opem@ecsim.site](mailto:opem@ecsim.site "opem@ecsim.site").

You can also join our discord server

## Outputs

1. [HTML](http://www.ecsim.site/opem/outputs/test.html)
2. [CSV](https://github.com/ECSIM/opem/blob/master/otherfile/test.csv)
3. [OPEM](https://github.com/ECSIM/opem/blob/master/otherfile/test.opem)

## Thanks

* [Chart.js](https://github.com/chartjs/Chart.js "Chartjs")
* [PyInstaller](https://github.com/pyinstaller/pyinstaller)
* [Draw.io](https://www.draw.io/)
* [Zahra Mobasher](https://www.instagram.com/littleblackoyster/?hl=en) (Logo design)

## Reference

1- J. C. Amphlett, R. M. Baumert, R. F. Mann, B. A. Peppley, and P. R. Roberge. 1995. "Performance Modeling of the Ballard Mark IV Solid Polymer Electrolyte Fuel Cell." J. Electrochem. Soc. (The Electrochemical Society, Inc.) 142 (1): 9-15. doi: 10.1149/1.2043959.

2- Jeferson M. Correa, Felix A. Farret, Vladimir A. Popov, Marcelo G. Simoes. 2005. "Sensitivity Analysis of the Modeling Parameters Used in Simulation of Proton Exchange Membrane Fuel Cells." IEEE Transactions on Energy Conversion (IEEE) 20 (1): 211-218. doi:10.1109/TEC.2004.842382.

3- Junbom Kim, Seong-Min Lee, Supramaniam Srinivasan, Charles E. Chamberlin. 1995. "Modeling of Proton Exchange Membrane Fuel Cell Performance with an Empirical Equation." Journal of The Electrochemical Society (The Electrochemical Society) 142 (8): 2670-2674. doi:10.1149/1.2050072.

4- I. Sadli, P. Thounthong, J.-P. Martin, S. Rael, B. Davat. 2006. "Behaviour of a PEMFC supplying a low voltage static converter." Journal of Power Sources (Elsevier) 156: 119–125. doi:10.1016/j.jpowsour.2005.08.021.

5- J. Padulles, G.W. Ault, J.R. McDonald. 2000. "An integrated SOFC plant dynamic model for power systems simulation." Journal of Power Sources (Elsevier) 86 (1-2): 495-500. doi:10.1016/S0378-7753(99)00430-9.

6- Hauer, K.-H. 2001. "Analysis tool for fuel cell vehicle hardware and software (controls) with an application to fuel economy comparisons of alternative system designs." Ph.D. dissertation, Transportation Technology
and Policy, University of California Davis.

7- A. Saadi, M. Becherif, A. Aboubou, M.Y. Ayad. 2013. "Comparison of proton exchange membrane fuel cell static models." Renewable Energy (Elsevier) 56: 64-71. doi:dx.doi.org/10.1016/j.renene.2012.10.012.

8- Diego Feroldi, Marta Basualdo. 2012. "Description of PEM Fuel Cells System." Green Energy and Technology (Springer) 49-72. doi:10.1007/978-1-84996-184-4_2

9- Gottesfeld, Shimshon. n.d. The Polymer Electrolyte Fuel Cell: Materials Issues in a Hydrogen Fueled Power Source.
 http://physics.oregonstate.edu/~hetheriw/energy/topics/doc/electrochemistry/fc/basic/The_Polymer_Electrolyte_Fuel_Cell.htm

10- Mohamed Becherif, Aïcha Saadi, Daniel Hissel, Abdennacer Aboubou, Mohamed Yacine Ayad. 2011.
 "Static and dynamic proton exchange membrane fuel cell models." Journal of Hydrocarbons Mines and Environmental Research 2 (1)

11- Larminie, J., Dicks, A., & McDonald, M. S. 2003. Fuel cell systems explained (Vol. 2, pp. 207-225). Chichester, UK: J. Wiley. doi: 10.1002/9781118706992.

12- Rho, Y. W., Srinivasan, S., & Kho, Y. T. 1994. ''Mass transport phenomena in proton exchange membrane fuel cells using o 2/he, o 2/ar, and o 2/n 2 mixtures ii. Theoretical analysis.'' Journal of the Electrochemical Society, 141(8), 2089-2096. doi: 10.1149/1.2055066.

13- U. Chakraborty, A New Model for Constant Fuel Utilization and Constant Fuel Flow in Fuel Cells, Appl. Sci. 9 (2019) 1066. https://doi.org/10.3390/app9061066.

## Cite

If you use OPEM in your research , please cite this paper :

@article{Haghighi2018,
  doi = {10.21105/joss.00676},
  url = {https://doi.org/10.21105/joss.00676},
  year  = {2018},
  month = {jul},
  publisher = {The Open Journal},
  volume = {3},
  number = {27},
  pages = {676},
  author = {Sepand Haghighi and Kasra Askari and Sarmin Hamidi and Mohammad Mahdi Rahimi},
  title = {{OPEM} : Open Source {PEM} Cell Simulation Tool},
  journal = {Journal of Open Source Software}
}

Download [OPEM.bib](http://www.ecsim.site/opem/OPEM.bib)(BibTeX Format)

		JOSS

		Zenodo

## Show Your Support

Star This Repo

Give a ⭐ if this project helped you!

Donate to Our Project

If you do like our project and we hope that you do, can you please support us? Our project is not and is never going to be working for profit. We need the money just so we can continue doing what we do ;-) .


|Docs| |PyPI| |Build Status| |CI Status| |Coverage Status| |Powered by Astropy Badge|

About
=====

The `reproject` package is a Python package to reproject astronomical
images using various techniques via a uniform interface. By
*reprojection*, we mean the re-gridding of images from one world
coordinate system to another (for example changing the pixel resolution,
orientation, coordinate system). Currently, we have implemented
reprojection of celestial images by interpolation (like
`SWARP <http://www.astromatic.net/software/swarp>`__), by the adaptive and
anti-aliased algorithm of `DeForest (2004)
<https://doi.org/10.1023/B:SOLA.0000021743.24248.b0>`_, and by finding the
exact overlap between pixels on the celestial sphere (like `Montage
<http://montage.ipac.caltech.edu/index.html>`__). It can also reproject to/from
HEALPIX projections by relying on the `astropy-healpix
<https://github.com/astropy/astropy-healpix>`__ package.

For more information, including on how to install the package, see
https://reproject.readthedocs.io

.. figure:: https://github.com/astrofrog/reproject/raw/master/docs/images/index-4.png
   :alt: screenshot

.. |Docs| image:: https://readthedocs.org/projects/reproject/badge/?version=latest
   :target: https://reproject.readthedocs.io/en/latest/?badge=latest
.. |PyPI| image:: https://img.shields.io/pypi/v/reproject.svg
   :target: https://pypi.python.org/pypi/reproject
.. |Build Status| image:: https://dev.azure.com/astropy-project/reproject/_apis/build/status/astropy.reproject?branchName=main
   :target: https://dev.azure.com/astropy-project/reproject/_build/latest?definitionId=3&branchName=main
.. |CI Status| image:: https://github.com/astropy/reproject/workflows/CI/badge.svg
   :target: https://github.com/astropy/reproject/actions
.. |Coverage Status| image:: https://codecov.io/gh/astropy/reproject/branch/main/graph/badge.svg
   :target: https://codecov.io/gh/astropy/reproject
.. |Powered by Astropy Badge| image:: http://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat
   :target: https://astropy.org