Python version support ====================== - Drop Python 2.7 support - Add testing for Python 3.4 and 3.9 - Set python version requirements in setup.py Package changes =============== - Split module up into a package with submodules - Break templates out into package resources - Incorporate server into same package - Use Enum for colors - Refresh example badges - Simplify documentation - Remove cli options and python api documentation from the README as it is hard to maintain, and often goes out-of-date - Add CONTRIBUTING.md
1
.gitignore
vendored
@@ -116,3 +116,4 @@ demo.svg
|
||||
htmlcov/
|
||||
|
||||
**/*.svg
|
||||
!anybadge/templates/*.svg
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
dist: xenial
|
||||
language: python
|
||||
python:
|
||||
- '2.7'
|
||||
- '3.4'
|
||||
- '3.5'
|
||||
- '3.6'
|
||||
- '3.7'
|
||||
- '3.8'
|
||||
- '3.9'
|
||||
install:
|
||||
- pip install -U setuptools pip -r build-requirements.txt
|
||||
script:
|
||||
- pytest --doctest-modules --cov=anybadge --cov-report html:htmlcov anybadge.py tests
|
||||
- pytest --doctest-modules --cov=anybadge --cov-report html:htmlcov anybadge tests
|
||||
before_deploy:
|
||||
- sed -i "s/^version = .*/version = __version__ = \"$TRAVIS_TAG\"/" anybadge.py
|
||||
deploy:
|
||||
@@ -21,4 +23,4 @@ deploy:
|
||||
on:
|
||||
tags: true
|
||||
all_branches: true
|
||||
python: '3.7'
|
||||
python: '3.9'
|
||||
64
CONTRIBUTING.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Contributing to anybadge
|
||||
I love your input! I want to make contributing to this project as easy and transparent as possible, whether it's:
|
||||
|
||||
- Reporting a bug
|
||||
- Discussing the current state of the code
|
||||
- Submitting a fix
|
||||
- Proposing new features
|
||||
- Becoming a maintainer
|
||||
|
||||
## I use [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow), so all code changes happen through pull requests
|
||||
Pull requests are the best way to propose changes to the codebase (I use
|
||||
[Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow)). I actively welcome your pull requests:
|
||||
|
||||
1. Fork the repo and create your branch from `master`
|
||||
2. If you've added code that should be tested, add tests
|
||||
3. If you've changed APIs, update the documentation
|
||||
4. Ensure the test suite passes
|
||||
5. Make sure your code lints (tbc)
|
||||
6. Issue that pull request!
|
||||
|
||||
## Any contributions you make will be under the MIT Software License
|
||||
When you submit code changes, your submissions are understood to be under the same
|
||||
[MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers
|
||||
if that's a concern.
|
||||
|
||||
## Report bugs using Github's [issues](https://github.com/jongracecox/anybadge/issues)
|
||||
I use GitHub issues to track public bugs. Report a bug by
|
||||
[opening a new issue](https://github.com/jongracecox/anybadge/issues/new/choose).
|
||||
|
||||
## Write bug reports with detail, background, and sample code
|
||||
**Great Bug Reports** tend to have:
|
||||
|
||||
- A quick summary and/or background
|
||||
- Steps to reproduce
|
||||
- Be specific!
|
||||
- Give sample code if you can (ideally sample code that *anyone* with a basic setup can run to reproduce)
|
||||
- What you expected would happen - (include explanation, screenshot, drawings, etc. to be exact)
|
||||
- What actually happens
|
||||
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
|
||||
|
||||
People *love* thorough bug reports.
|
||||
|
||||
## Use a Consistent Coding Style
|
||||
Please follow the existing coding style.
|
||||
|
||||
## License
|
||||
By contributing, you agree that your contributions will be licensed under its MIT License.
|
||||
|
||||
# Technical stuff
|
||||
|
||||
## Documentation
|
||||
The `README.md` file contains a table showing example badges for the different built-in colors. If you modify the
|
||||
appearance of badges, or the available colors please update the table using the following code:
|
||||
|
||||
```python
|
||||
import anybadge
|
||||
print("""| Color Name | Hex Code | Example |
|
||||
| ---------- | -------- | ------- |""")
|
||||
for color in sorted([c for c in anybadge.colors.Color], key=lambda x: x.name):
|
||||
file = 'examples/color_' + color.name.lower() + '.svg'
|
||||
url = 'https://cdn.rawgit.com/jongracecox/anybadge/master/' + file
|
||||
anybadge.Badge(label='Color', value=color, default_color=color.name.lower()).write_badge(file, overwrite=True)
|
||||
print("| {color} | {hex} |  |".format(color=color.name.lower(), hex=color.value.upper(), url=url))
|
||||
```
|
||||
407
README.md
@@ -11,6 +11,8 @@ Python project for generating badges for your projects
|
||||
|
||||
[](https://www.buymeacoffee.com/jongracecox)
|
||||
|
||||
Supports: Python 3.4-3.9 (2.7 support has been dropped)
|
||||
|
||||
## Overview
|
||||
|
||||
`anybadge` can be used to add badge generation to your Python projects,
|
||||
@@ -140,12 +142,12 @@ Available named colors are:
|
||||
| aqua | #00FFFF |  |
|
||||
| black | #000000 |  |
|
||||
| blue | #0000FF |  |
|
||||
| brightred | #FF0000 |  |
|
||||
| brightyellow | #FFFF00 |  |
|
||||
| bright_red | #FF0000 |  |
|
||||
| bright_yellow | #FFFF00 |  |
|
||||
| fuchsia | #FF00FF |  |
|
||||
| gray | #808080 |  |
|
||||
| green | #4C1 |  |
|
||||
| lightgrey | #9F9F9F |  |
|
||||
| light_grey | #9F9F9F |  |
|
||||
| lime | #00FF00 |  |
|
||||
| maroon | #800000 |  |
|
||||
| navy | #000080 |  |
|
||||
@@ -157,19 +159,7 @@ Available named colors are:
|
||||
| teal | #008080 |  |
|
||||
| white | #FFFFFF |  |
|
||||
| yellow | #DFB317 |  |
|
||||
| yellowgreen | #A4A61D |  |
|
||||
|
||||
This table was generated with the following code:
|
||||
|
||||
```python
|
||||
print("""| Color Name | Hex Code | Example |
|
||||
| ---------- | -------- | ------- |""")
|
||||
for color, hex in sorted(anybadge.COLORS.items()):
|
||||
file = 'examples/color_' + color + '.svg'
|
||||
url = 'https://cdn.rawgit.com/jongracecox/anybadge/master/' + file
|
||||
anybadge.Badge(label='Color', value=color, default_color=color).write_badge(file, overwrite=True)
|
||||
print("| {color} | {hex} |  |".format(color=color, hex=hex.upper(), url=url))
|
||||
```
|
||||
| yellow_green | #A4A61D |  |
|
||||
|
||||
### Semantic version support
|
||||
|
||||
@@ -261,60 +251,9 @@ test1 = Badge(
|
||||
test1.write_badge('test1.svg')
|
||||
```
|
||||
|
||||
### Options
|
||||
### Command-line options
|
||||
|
||||
These are the command line options:
|
||||
|
||||
```
|
||||
positional arguments:
|
||||
args Pairs of <upper>=<color>. For example 2=red 4=orange
|
||||
6=yellow 8=good. Read this as "Less than 2 = red, less
|
||||
than 4 = orange...".
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-l LABEL, --label LABEL
|
||||
The badge label.
|
||||
-v VALUE, --value VALUE
|
||||
The badge value.
|
||||
-m VALUE_FORMAT, --value-format VALUE_FORMAT
|
||||
Formatting string for value (e.g. "%.2f" for 2dp
|
||||
floats)
|
||||
-c COLOR, --color COLOR
|
||||
For fixed color badges use --colorto specify the badge
|
||||
color.
|
||||
-p PREFIX, --prefix PREFIX
|
||||
Optional prefix for value.
|
||||
-s SUFFIX, --suffix SUFFIX
|
||||
Optional suffix for value.
|
||||
-d PADDING, --padding PADDING
|
||||
Number of characters to pad on either side of the
|
||||
badge text.
|
||||
-lp LABEL_PADDING, --label-padding LABEL_PADDING
|
||||
Number of characters to pad on either side of the
|
||||
badge label.
|
||||
-vp VALUE_PADDING, --value-padding VALUE_PADDING
|
||||
Number of characters to pad on either side of the
|
||||
badge value.
|
||||
-n FONT, --font FONT "DejaVu Sans,Verdana,Geneva,sans-serif"Font name.
|
||||
Supported fonts: ,"Arial, Helvetica, sans-serif"
|
||||
-z FONT_SIZE, --font-size FONT_SIZE
|
||||
Font size.
|
||||
-t TEMPLATE, --template TEMPLATE
|
||||
Location of alternative template .svg file.
|
||||
-s STYLE, --style STYLE
|
||||
Alternative style of badge to create. Valid values are
|
||||
"gitlab-scoped", "default". This overrides any templates
|
||||
passed using --template.
|
||||
-u, --use-max Use the maximum threshold color when the value exceeds
|
||||
the maximum threshold.
|
||||
-f FILE, --file FILE Output file location.
|
||||
-o, --overwrite Overwrite output file if it already exists.
|
||||
-r TEXT_COLOR, --text-color TEXT_COLOR
|
||||
Text color. Single value affects both labeland value
|
||||
colors. A comma separated pair affects label and value
|
||||
text respectively.
|
||||
```
|
||||
The command line options can be viewed using `anybadge --help`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -344,330 +283,10 @@ anybadge.py --label=pipeline --value=passing --file=pipeline.svg passing=green f
|
||||
|
||||
Python usage
|
||||
============
|
||||
Here is the output of ``help(anybadge)``::
|
||||
|
||||
```
|
||||
Help on module anybadge:
|
||||
|
||||
NAME
|
||||
anybadge - anybadge
|
||||
|
||||
DESCRIPTION
|
||||
A Python module for generating badges for your projects, with a focus on
|
||||
simplicity and flexibility.
|
||||
|
||||
CLASSES
|
||||
builtins.object
|
||||
Badge
|
||||
|
||||
class Badge(builtins.object)
|
||||
| Badge(label, value, font_name=None, font_size=None, num_padding_chars=None, num_label_padding_chars=None, num_value_padding_chars=None, template=None, style=None, value_prefix='', value_suffix='', thresholds=None, default_color=None, use_max_when_value_exceeds=True, value_format=None, text_color=None, semver=False)
|
||||
|
|
||||
| Badge class used to generate badges.
|
||||
|
|
||||
| Args:
|
||||
| label(str): Badge label text.
|
||||
| value(str): Badge value text.
|
||||
| font_name(str, optional): Name of font to use.
|
||||
| font_size(int, optional): Font size.
|
||||
| num_padding_chars(float, optional): Number of padding characters to use to give extra
|
||||
| space around text.
|
||||
| num_label_padding_chars(float, optional): Number of padding characters to use to give extra
|
||||
| space around label text.
|
||||
| num_value_padding_chars(float, optional): Number of padding characters to use to give extra
|
||||
| space around value text.
|
||||
| template(str, optional): String containing the SVG template. This should be valid SVG
|
||||
| file content with place holders for variables to be populated during rendering.
|
||||
| style(str, optional): Style of badge to create. This will make anybadge render a badge in a
|
||||
| different style. Valid values are "gitlab-scoped", "default". Default is "default".
|
||||
| value_prefix(str, optional): Prefix to be placed before value.
|
||||
| value_suffix(str, optional): Suffix to be placed after value.
|
||||
| thresholds(dict, optional): A dictionary containing thresholds used to select badge
|
||||
| color based on the badge value.
|
||||
| default_color(str, optional): Badge color as a name or as an HTML color code.
|
||||
| use_max_when_value_exceeds(bool, optional): Choose whether to use the maximum threshold
|
||||
| value when the badge value exceeds the top threshold. Default is True.
|
||||
| value_format(str, optional) String with formatting to be used to format the value text.
|
||||
| text_color(str, optional): Text color as a name or as an HTML color code.
|
||||
| semver(bool, optional): Used to indicate that the value is a semantic version number.
|
||||
|
|
||||
| Examples:
|
||||
|
|
||||
| Create a simple green badge:
|
||||
|
|
||||
| >>> badge = Badge('label', 123, default_color='green')
|
||||
|
|
||||
| Write a badge to file, overwriting any existing file:
|
||||
|
|
||||
| >>> badge = Badge('label', 123, default_color='green')
|
||||
| >>> badge.write_badge('demo.svg', overwrite=True)
|
||||
|
|
||||
| Here are a number of examples showing thresholds, since there
|
||||
| are certain situations that may not be obvious:
|
||||
|
|
||||
| >>> badge = Badge('pipeline', 'passing', thresholds={'passing': 'green', 'failing': 'red'})
|
||||
| >>> badge.badge_color
|
||||
| 'green'
|
||||
|
|
||||
| 2.32 is not <2
|
||||
| 2.32 is < 4, so 2.32 yields orange
|
||||
| >>> badge = Badge('pylint', 2.32, thresholds={2: 'red',
|
||||
| ... 4: 'orange',
|
||||
| ... 8: 'yellow',
|
||||
| ... 10: 'green'})
|
||||
| >>> badge.badge_color
|
||||
| 'orange'
|
||||
|
|
||||
| 8 is not <8
|
||||
| 8 is <4, so 8 yields orange
|
||||
| >>> badge = Badge('pylint', 8, thresholds={2: 'red',
|
||||
| ... 4: 'orange',
|
||||
| ... 8: 'yellow',
|
||||
| ... 10: 'green'})
|
||||
| >>> badge.badge_color
|
||||
| 'green'
|
||||
|
|
||||
| 10 is not <8, but use_max_when_value_exceeds defaults to
|
||||
| True, so 10 yields green
|
||||
| >>> badge = Badge('pylint', 11, thresholds={2: 'red',
|
||||
| ... 4: 'orange',
|
||||
| ... 8: 'yellow',
|
||||
| ... 10: 'green'})
|
||||
| >>> badge.badge_color
|
||||
| 'green'
|
||||
|
|
||||
| 11 is not <10, and use_max_when_value_exceeds is set to
|
||||
| False, so 11 yields the default color '#4c1'
|
||||
| >>> badge = Badge('pylint', 11, use_max_when_value_exceeds=False,
|
||||
| ... thresholds={2: 'red', 4: 'orange', 8: 'yellow',
|
||||
| ... 10: 'green'})
|
||||
| >>> badge.badge_color
|
||||
| '#4c1'
|
||||
|
|
||||
| Methods defined here:
|
||||
|
|
||||
| __init__(self, label, value, font_name=None, font_size=None, num_padding_chars=None, num_label_padding_chars=None, num_value_padding_chars=None, template=None, style=None, value_prefix='', value_suffix='', thresholds=None, default_color=None, use_max_when_value_exceeds=True, value_format=None, text_color=None, semver=False)
|
||||
| Constructor for Badge class.
|
||||
|
|
||||
| __repr__(self)
|
||||
| Return a representation of the Badge object instance.
|
||||
|
|
||||
| The output of the __repr__ function could be used to recreate the current object.
|
||||
|
|
||||
| Examples:
|
||||
|
|
||||
| >>> badge = Badge('example', '123.456')
|
||||
| >>> repr(badge)
|
||||
| "Badge('example', '123.456')"
|
||||
|
|
||||
| >>> badge = Badge('example', '123.456', value_suffix='TB')
|
||||
| >>> repr(badge)
|
||||
| "Badge('example', '123.456', value_suffix='TB')"
|
||||
|
|
||||
| >>> badge = Badge('example', '123.456', text_color='#111111', value_suffix='TB')
|
||||
| >>> repr(badge)
|
||||
| "Badge('example', '123.456', value_suffix='TB', text_color='#111111')"
|
||||
|
|
||||
| >>> badge = Badge('example', '123', num_padding_chars=5)
|
||||
| >>> repr(badge)
|
||||
| "Badge('example', '123', num_padding_chars=5)"
|
||||
|
|
||||
| >>> badge = Badge('example', '123', num_label_padding_chars=5)
|
||||
| >>> repr(badge)
|
||||
| "Badge('example', '123', num_label_padding_chars=5)"
|
||||
|
|
||||
| >>> badge = Badge('example', '123', num_label_padding_chars=5, num_value_padding_chars=6,
|
||||
| ... template='template.svg', value_prefix='$', thresholds={10: 'green', 30: 'red'},
|
||||
| ... default_color='red', use_max_when_value_exceeds=False, value_format="%s m/s")
|
||||
| >>> repr(badge)
|
||||
| "Badge('example', '123', num_label_padding_chars=5, num_value_padding_chars=6, template='template.svg', value_prefix='$', thresholds={10: 'green', 30: 'red'}, default_color='red', use_max_when_value_exceeds=False, value_format='%s m/s')"
|
||||
|
|
||||
| __str__(self)
|
||||
| Return string representation of badge.
|
||||
|
|
||||
| This will return the badge SVG text.
|
||||
|
|
||||
| Returns: str
|
||||
|
|
||||
| Examples:
|
||||
|
|
||||
| >>> print(Badge('example', '123')) # doctest: +ELLIPSIS
|
||||
| <?xml version="1.0" encoding="UTF-8"?>
|
||||
| ...
|
||||
|
|
||||
| get_text_width(self, text)
|
||||
| Return the width of text.
|
||||
|
|
||||
| Args:
|
||||
| text(str): Text to get the pixel width of.
|
||||
|
|
||||
| Returns:
|
||||
| int: Pixel width of the given text based on the badges selected font.
|
||||
|
|
||||
| This implementation assumes a fixed font of:
|
||||
|
|
||||
| font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"
|
||||
| >>> badge = Badge('x', 1, font_name='DejaVu Sans,Verdana,Geneva,sans-serif', font_size=11)
|
||||
| >>> badge.get_text_width('pylint')
|
||||
| 34
|
||||
|
|
||||
| write_badge(self, file_path, overwrite=False)
|
||||
| Write badge to file.
|
||||
|
|
||||
| ----------------------------------------------------------------------
|
||||
| Readonly properties defined here:
|
||||
|
|
||||
| arc_start
|
||||
| The position where the arc on the gitlab-scoped should start.
|
||||
|
|
||||
| Returns: int
|
||||
|
|
||||
| Examples:
|
||||
|
|
||||
| >>> badge = Badge('pylint', '5')
|
||||
| >>> badge.arc_start
|
||||
| 58
|
||||
|
|
||||
| badge_color
|
||||
| Badge color based on the configured thresholds.
|
||||
|
|
||||
| Returns: str
|
||||
|
|
||||
| badge_color_code
|
||||
| Return the color code for the badge.
|
||||
|
|
||||
| Returns: str
|
||||
|
|
||||
| Raises: ValueError when an invalid badge color is set.
|
||||
|
|
||||
| badge_svg_text
|
||||
| The badge SVG text.
|
||||
|
|
||||
| Returns: str
|
||||
|
|
||||
| badge_width
|
||||
| The total width of badge.
|
||||
|
|
||||
| Returns: int
|
||||
|
|
||||
| Examples:
|
||||
|
|
||||
| >>> badge = Badge('pylint', '5')
|
||||
| >>> badge.badge_width
|
||||
| 61
|
||||
|
|
||||
| color_split_position
|
||||
| The SVG x position where the color split should occur.
|
||||
|
|
||||
| Returns: int
|
||||
|
|
||||
| float_thresholds
|
||||
| Thresholds as a dict using floats as keys.
|
||||
|
|
||||
| font_width
|
||||
| Return the width multiplier for a font.
|
||||
|
|
||||
| Returns:
|
||||
| int: Maximum pixel width of badges selected font.
|
||||
|
|
||||
| Example:
|
||||
|
|
||||
| >>> Badge(label='x', value='1').font_width
|
||||
| 10
|
||||
|
|
||||
| label_anchor
|
||||
| The SVG x position of the middle anchor for the label text.
|
||||
|
|
||||
| Returns: float
|
||||
|
|
||||
| label_anchor_shadow
|
||||
| The SVG x position of the label shadow anchor.
|
||||
|
|
||||
| Returns: float
|
||||
|
|
||||
| label_width
|
||||
| The SVG width of the label text.
|
||||
|
|
||||
| Returns: int
|
||||
|
|
||||
| semver_thresholds
|
||||
| Thresholds as a dict using LooseVersion as keys.
|
||||
|
|
||||
| semver_version
|
||||
| The semantic version represented by the value string.
|
||||
|
|
||||
| Returns: LooseVersion
|
||||
|
|
||||
| value_anchor
|
||||
| The SVG x position of the middle anchor for the value text.
|
||||
|
|
||||
| Returns: float
|
||||
|
|
||||
| value_anchor_shadow
|
||||
| The SVG x position of the value shadow anchor.
|
||||
|
|
||||
| Returns: float
|
||||
|
|
||||
| value_box_width
|
||||
| The SVG width of the value text box.
|
||||
|
|
||||
| Returns: int
|
||||
|
|
||||
| value_is_float
|
||||
| Identify whether the value text is a float.
|
||||
|
|
||||
| Returns: bool
|
||||
|
|
||||
| value_is_int
|
||||
| Identify whether the value text is an int.
|
||||
|
|
||||
| Returns: bool
|
||||
|
|
||||
| value_type
|
||||
| The Python type associated with the value.
|
||||
|
|
||||
| Returns: type
|
||||
|
|
||||
| value_width
|
||||
| The SVG width of the value text.
|
||||
|
|
||||
| Returns: int
|
||||
|
|
||||
| ----------------------------------------------------------------------
|
||||
| Data descriptors defined here:
|
||||
|
|
||||
| __dict__
|
||||
| dictionary for instance variables (if defined)
|
||||
|
|
||||
| __weakref__
|
||||
| list of weak references to the object (if defined)
|
||||
|
||||
FUNCTIONS
|
||||
main(args=None)
|
||||
Generate a badge based on command line arguments.
|
||||
|
||||
parse_args(args)
|
||||
Parse the command line arguments.
|
||||
|
||||
DATA
|
||||
BADGE_TEMPLATES = {'coverage': {'label': 'coverage', 'suffix': '%', 't...
|
||||
COLORS = {'aqua': '#00FFFF', 'black': '#000000', 'blue': '#0000FF', 'b...
|
||||
DEFAULT_COLOR = '#4c1'
|
||||
DEFAULT_FONT = 'DejaVu Sans,Verdana,Geneva,sans-serif'
|
||||
DEFAULT_FONT_SIZE = 11
|
||||
DEFAULT_TEXT_COLOR = '#fff'
|
||||
FONT_WIDTHS = {'Arial, Helvetica, sans-serif': {11: 8}, 'DejaVu Sans,V...
|
||||
MASK_ID_PREFIX = 'anybadge_'
|
||||
NUM_PADDING_CHARS = 0.5
|
||||
TEMPLATE_GITLAB_SCOPED_SVG = '<?xml version="1.0" encoding="UTF-8"?>\n...
|
||||
TEMPLATE_SVG = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmln...ho...
|
||||
VERSION_COMPARISON_SUPPORTED = True
|
||||
__summary__ = 'A simple, flexible badge generator.'
|
||||
__title__ = 'anybadge'
|
||||
__uri__ = 'https://github.com/jongracecox/anybadge'
|
||||
__version_info__ = ('0', '0', '0')
|
||||
version = '0.0.0'
|
||||
|
||||
VERSION
|
||||
0.0.0
|
||||
For Python API details you can use the inbuilt documentation:
|
||||
|
||||
```python
|
||||
from anybadge import badge
|
||||
help(badge)
|
||||
```
|
||||
|
||||
11
anybadge/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import re
|
||||
from .badge import Badge
|
||||
from .colors import Color
|
||||
from .styles import Style
|
||||
|
||||
# Package information
|
||||
version = __version__ = "0.0.0"
|
||||
__version_info__ = tuple(re.split('[.-]', __version__))
|
||||
__title__ = "anybadge"
|
||||
__summary__ = "A simple, flexible badge generator."
|
||||
__uri__ = "https://github.com/jongracecox/anybadge"
|
||||
493
anybadge.py → anybadge/badge.py
Executable file → Normal file
@@ -1,147 +1,20 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
anybadge
|
||||
|
||||
A Python module for generating badges for your projects, with a focus on
|
||||
simplicity and flexibility.
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import argparse
|
||||
import textwrap
|
||||
from collections import OrderedDict
|
||||
|
||||
from . import config
|
||||
from .colors import Color
|
||||
from .exceptions import UnknownBadgeTemplate
|
||||
|
||||
from .helpers import _get_approx_string_width
|
||||
|
||||
|
||||
# Try and obtain packaging package to support version comparison.
|
||||
try:
|
||||
from distutils.version import LooseVersion
|
||||
VERSION_COMPARISON_SUPPORTED = True
|
||||
except ImportError:
|
||||
VERSION_COMPARISON_SUPPORTED = False
|
||||
from .templates import get_template
|
||||
|
||||
from packaging.version import Version
|
||||
|
||||
|
||||
# Package information
|
||||
version = __version__ = "0.0.0"
|
||||
__version_info__ = tuple(re.split('[.-]', __version__))
|
||||
__title__ = "anybadge"
|
||||
__summary__ = "A simple, flexible badge generator."
|
||||
__uri__ = "https://github.com/jongracecox/anybadge"
|
||||
|
||||
|
||||
# Set some defaults
|
||||
DEFAULT_FONT = 'DejaVu Sans,Verdana,Geneva,sans-serif'
|
||||
DEFAULT_FONT_SIZE = 11
|
||||
NUM_PADDING_CHARS = 0.5
|
||||
DEFAULT_COLOR = '#4c1'
|
||||
DEFAULT_TEXT_COLOR = '#fff'
|
||||
MASK_ID_PREFIX = 'anybadge_'
|
||||
|
||||
# Dictionary for looking up approx pixel widths of
|
||||
# supported fonts and font sizes.
|
||||
FONT_WIDTHS = {
|
||||
'DejaVu Sans,Verdana,Geneva,sans-serif': {
|
||||
10: 9,
|
||||
11: 10,
|
||||
12: 11,
|
||||
},
|
||||
'Arial, Helvetica, sans-serif': {
|
||||
11: 8,
|
||||
},
|
||||
}
|
||||
|
||||
# Create a dictionary of colors to make selections
|
||||
# easier.
|
||||
COLORS = {
|
||||
'white': '#FFFFFF',
|
||||
'silver': '#C0C0C0',
|
||||
'gray': '#808080',
|
||||
'black': '#000000',
|
||||
'red': '#e05d44',
|
||||
'brightred': '#FF0000',
|
||||
'maroon': '#800000',
|
||||
'olive': '#808000',
|
||||
'lime': '#00FF00',
|
||||
'brightyellow': '#FFFF00',
|
||||
'yellow': '#dfb317',
|
||||
'green': '#4c1',
|
||||
'yellowgreen': '#a4a61d',
|
||||
'aqua': '#00FFFF',
|
||||
'teal': '#008080',
|
||||
'blue': '#0000FF',
|
||||
'navy': '#000080',
|
||||
'fuchsia': '#FF00FF',
|
||||
'purple': '#800080',
|
||||
'orange': '#fe7d37',
|
||||
'lightgrey': '#9f9f9f',
|
||||
}
|
||||
|
||||
# Template SVG with placeholders for various items that
|
||||
# will be added during final creation.
|
||||
TEMPLATE_SVG = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="{{ mask id }}">
|
||||
<rect width="{{ badge width }}" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#{{ mask id }})">
|
||||
<path fill="#555" d="M0 0h{{ color split x }}v20H0z"/>
|
||||
<path fill="{{ color }}" d="M{{ color split x }} 0h{{ value width }}v20H{{ color split x }}z"/>
|
||||
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
|
||||
</g>
|
||||
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ label anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ label }}</text>
|
||||
<text x="{{ label anchor }}" y="14">{{ label }}</text>
|
||||
</g>
|
||||
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ value anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ value }}</text>
|
||||
<text x="{{ value anchor }}" y="14">{{ value }}</text>
|
||||
</g>
|
||||
</svg>"""
|
||||
|
||||
# Template SVG for GitLab Scoped label and value badges with placeholders for
|
||||
# various items that will be added during final creation.
|
||||
TEMPLATE_GITLAB_SCOPED_SVG = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="{{ mask id }}">
|
||||
<rect width="{{ badge width }}" height="20" rx="10" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#{{ mask id }})">
|
||||
<path fill="{{ color }}" d="M0 0h{{ badge width }}v20H0z"/>
|
||||
<path fill="#262626" d="M{{ color split x }} 2h{{ value box width }}v16H{{ color split x }}z"/>
|
||||
<path fill="#262626" d="M{{ arc start }},18 a1,1 0 0,0 0,-16"/>
|
||||
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
|
||||
</g>
|
||||
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ label anchor }}" y="14">{{ label }}</text>
|
||||
</g>
|
||||
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ value anchor }}" y="14">{{ value }}</text>
|
||||
</g>
|
||||
</svg>"""
|
||||
|
||||
# Define some templates that can be used for common badge types, saving
|
||||
# from having to provide thresholds and labels each time.
|
||||
BADGE_TEMPLATES = {
|
||||
'pylint': {
|
||||
'threshold': '2=red 4=orange 8=yellow 10=green',
|
||||
'label': 'pylint'
|
||||
},
|
||||
'coverage': {
|
||||
'threshold': '50=red 60=orange 80=yellow 100=green',
|
||||
'label': 'coverage',
|
||||
'suffix': '%'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Badge(object):
|
||||
class Badge:
|
||||
"""
|
||||
Badge class used to generate badges.
|
||||
|
||||
@@ -234,34 +107,32 @@ class Badge(object):
|
||||
"""Constructor for Badge class."""
|
||||
# Set defaults if values were not passed
|
||||
if not font_name:
|
||||
font_name = DEFAULT_FONT
|
||||
font_name = config.DEFAULT_FONT
|
||||
if not font_size:
|
||||
font_size = DEFAULT_FONT_SIZE
|
||||
font_size = config.DEFAULT_FONT_SIZE
|
||||
if num_label_padding_chars is None:
|
||||
if num_padding_chars is None:
|
||||
num_label_padding_chars = NUM_PADDING_CHARS
|
||||
num_label_padding_chars = config.NUM_PADDING_CHARS
|
||||
else:
|
||||
num_label_padding_chars = num_padding_chars
|
||||
if num_value_padding_chars is None:
|
||||
if num_padding_chars is None:
|
||||
num_value_padding_chars = NUM_PADDING_CHARS
|
||||
num_value_padding_chars = config.NUM_PADDING_CHARS
|
||||
else:
|
||||
num_value_padding_chars = num_padding_chars
|
||||
if not template:
|
||||
template = TEMPLATE_SVG
|
||||
template = get_template('default')
|
||||
if style not in ['gitlab-scoped']:
|
||||
style = "default"
|
||||
if not default_color:
|
||||
default_color = DEFAULT_COLOR
|
||||
default_color = config.DEFAULT_COLOR
|
||||
if not text_color:
|
||||
text_color = DEFAULT_TEXT_COLOR
|
||||
text_color = config.DEFAULT_TEXT_COLOR
|
||||
|
||||
self.label = label
|
||||
self.value = value
|
||||
|
||||
self.value_is_version = semver
|
||||
if self.value_is_version and not VERSION_COMPARISON_SUPPORTED:
|
||||
raise RuntimeError("Version comparison is not supported.")
|
||||
|
||||
self.value_format = value_format
|
||||
if value_format:
|
||||
@@ -272,8 +143,11 @@ class Badge(object):
|
||||
self.value_suffix = value_suffix
|
||||
self.value_text = value_prefix + value_text + value_suffix
|
||||
|
||||
if font_name not in FONT_WIDTHS:
|
||||
raise ValueError('Font name "%s" not found. Available fonts: %s' % (font_name, ', '.join(FONT_WIDTHS.keys())))
|
||||
if font_name not in config.FONT_WIDTHS:
|
||||
raise ValueError(
|
||||
'Font name "%s" not found. '
|
||||
'Available fonts: %s' % (font_name, ', '.join(config.FONT_WIDTHS.keys()))
|
||||
)
|
||||
self.font_name = font_name
|
||||
self.font_size = font_size
|
||||
self.num_label_padding_chars = num_label_padding_chars
|
||||
@@ -329,19 +203,19 @@ class Badge(object):
|
||||
|
||||
"""
|
||||
optional_args = ""
|
||||
if self.font_name != DEFAULT_FONT:
|
||||
if self.font_name != config.DEFAULT_FONT:
|
||||
optional_args += ", font_name=%s" % repr(self.font_name)
|
||||
if self.font_size != DEFAULT_FONT_SIZE:
|
||||
if self.font_size != config.DEFAULT_FONT_SIZE:
|
||||
optional_args += ", font_size=%s" % repr(self.font_size)
|
||||
if self.num_label_padding_chars == self.num_value_padding_chars:
|
||||
if self.num_label_padding_chars != NUM_PADDING_CHARS:
|
||||
if self.num_label_padding_chars != config.NUM_PADDING_CHARS:
|
||||
optional_args += ", num_padding_chars=%s" % repr(self.num_label_padding_chars)
|
||||
else:
|
||||
if self.num_label_padding_chars != NUM_PADDING_CHARS:
|
||||
if self.num_label_padding_chars != config.NUM_PADDING_CHARS:
|
||||
optional_args += ", num_label_padding_chars=%s" % repr(self.num_label_padding_chars)
|
||||
if self.num_value_padding_chars != NUM_PADDING_CHARS:
|
||||
if self.num_value_padding_chars != config.NUM_PADDING_CHARS:
|
||||
optional_args += ", num_value_padding_chars=%s" % repr(self.num_value_padding_chars)
|
||||
if self.template != TEMPLATE_SVG:
|
||||
if self.template != get_template('default'):
|
||||
optional_args += ", template=%s" % repr(self.template)
|
||||
if self.style != 'default':
|
||||
optional_args += ", style=%s" % repr(self.style)
|
||||
@@ -351,13 +225,13 @@ class Badge(object):
|
||||
optional_args += ", value_suffix=%s" % repr(self.value_suffix)
|
||||
if self.thresholds:
|
||||
optional_args += ", thresholds=%s" % repr(self.thresholds)
|
||||
if self.default_color != DEFAULT_COLOR:
|
||||
if self.default_color != config.DEFAULT_COLOR:
|
||||
optional_args += ", default_color=%s" % repr(self.default_color)
|
||||
if not self.use_max_when_value_exceeds:
|
||||
optional_args += ", use_max_when_value_exceeds=%s" % repr(self.use_max_when_value_exceeds)
|
||||
if self.value_format:
|
||||
optional_args += ", value_format=%s" % repr(self.value_format)
|
||||
if self.text_color != DEFAULT_TEXT_COLOR:
|
||||
if self.text_color != config.DEFAULT_TEXT_COLOR:
|
||||
optional_args += ", text_color=%s" % repr(self.text_color)
|
||||
|
||||
return "%s(%s, %s%s)" % (
|
||||
@@ -375,7 +249,6 @@ class Badge(object):
|
||||
"""
|
||||
return self.badge_svg_text
|
||||
|
||||
|
||||
@classmethod
|
||||
def _get_next_mask_id(cls):
|
||||
"""Return a new mask ID from a singleton sequence maintained on the class.
|
||||
@@ -387,7 +260,7 @@ class Badge(object):
|
||||
|
||||
cls.mask_id += 1
|
||||
|
||||
return MASK_ID_PREFIX + str(cls.mask_id)
|
||||
return config.MASK_ID_PREFIX + str(cls.mask_id)
|
||||
|
||||
def _get_svg_template(self):
|
||||
"""Return the correct SVG template to render, based on the style and template
|
||||
@@ -396,10 +269,16 @@ class Badge(object):
|
||||
Returns: str
|
||||
"""
|
||||
if self.style == "gitlab-scoped":
|
||||
return TEMPLATE_GITLAB_SCOPED_SVG
|
||||
return get_template('gitlab_scoped')
|
||||
|
||||
# Identify whether template is a file or the actual template text
|
||||
|
||||
if len(self.template.split('\n')) == 1:
|
||||
try:
|
||||
return get_template(self.template)
|
||||
except UnknownBadgeTemplate:
|
||||
pass
|
||||
|
||||
with open(self.template, mode='r') as file_handle:
|
||||
return file_handle.read()
|
||||
else:
|
||||
@@ -409,16 +288,16 @@ class Badge(object):
|
||||
def semver_version(self):
|
||||
"""The semantic version represented by the value string.
|
||||
|
||||
Returns: LooseVersion
|
||||
Returns: Version
|
||||
"""
|
||||
return LooseVersion(self.value)
|
||||
return Version(self.value)
|
||||
|
||||
@property
|
||||
def semver_thresholds(self):
|
||||
"""Thresholds as a dict using LooseVersion as keys."""
|
||||
# LooseVersion is not a hashable type, so can't be used to create an
|
||||
"""Thresholds as a dict using Version as keys."""
|
||||
# Version is not a hashable type, so can't be used to create an
|
||||
# ordered dict directly. First we need to create an ordered list of keys
|
||||
ordered_keys = sorted(self.thresholds.keys(), key=LooseVersion)
|
||||
ordered_keys = sorted(self.thresholds.keys(), key=Version)
|
||||
return OrderedDict((key, self.thresholds[key]) for key in ordered_keys)
|
||||
|
||||
@property
|
||||
@@ -441,7 +320,7 @@ class Badge(object):
|
||||
|
||||
try:
|
||||
_ = float(self.value)
|
||||
except ValueError:
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -455,7 +334,7 @@ class Badge(object):
|
||||
try:
|
||||
a = float(self.value)
|
||||
b = int(self.value)
|
||||
except ValueError:
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
else:
|
||||
return a == b
|
||||
@@ -467,7 +346,7 @@ class Badge(object):
|
||||
Returns: type
|
||||
"""
|
||||
if self.value_is_version:
|
||||
return LooseVersion
|
||||
return Version
|
||||
if self.value_is_float:
|
||||
return float
|
||||
elif self.value_is_int:
|
||||
@@ -511,7 +390,7 @@ class Badge(object):
|
||||
>>> Badge(label='x', value='1').font_width
|
||||
10
|
||||
"""
|
||||
return FONT_WIDTHS[self.font_name][self.font_size]
|
||||
return config.FONT_WIDTHS[self.font_name][self.font_size]
|
||||
|
||||
@property
|
||||
def color_split_position(self):
|
||||
@@ -658,7 +537,7 @@ class Badge(object):
|
||||
|
||||
# Set value and thresholds based on the value type. This will result in either
|
||||
# value and thresholds as floats or value and thresholds as semantic versions.
|
||||
if self.value_type == LooseVersion:
|
||||
if self.value_type == Version:
|
||||
value = self.semver_version
|
||||
thresholds = self.semver_thresholds
|
||||
else:
|
||||
@@ -682,7 +561,7 @@ class Badge(object):
|
||||
return self.default_color
|
||||
|
||||
@property
|
||||
def badge_color_code(self):
|
||||
def badge_color_code(self) -> str:
|
||||
"""Return the color code for the badge.
|
||||
|
||||
Returns: str
|
||||
@@ -690,13 +569,36 @@ class Badge(object):
|
||||
Raises: ValueError when an invalid badge color is set.
|
||||
"""
|
||||
color = self.badge_color
|
||||
|
||||
if isinstance(color, Color):
|
||||
return color.value
|
||||
|
||||
if color[0] == '#':
|
||||
return color
|
||||
|
||||
color = color.upper()
|
||||
|
||||
prefixes = ["BRIGHT", "YELLOW", "LIGHT"]
|
||||
|
||||
try:
|
||||
return COLORS[color]
|
||||
return Color[color.upper()].value
|
||||
except KeyError:
|
||||
raise ValueError('Invalid color code "%s". Valid color codes are: %s', (color, ", ".join(COLORS.keys())))
|
||||
pass
|
||||
|
||||
# For backward compatibility with old color names (that were lowercase and didn't
|
||||
# contain underscores) we will try to get the same color.
|
||||
|
||||
for prefix in prefixes:
|
||||
if color.startswith(prefix) and color != prefix and '_' not in color:
|
||||
try:
|
||||
return Color[color.replace(prefix, prefix + '_')].value
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
raise ValueError(
|
||||
'Invalid color code "%s". '
|
||||
'Valid color codes are: %s', (color, ", ".join(list(Color.__members__.keys())))
|
||||
)
|
||||
|
||||
def write_badge(self, file_path, overwrite=False):
|
||||
"""Write badge to file."""
|
||||
@@ -716,248 +618,3 @@ class Badge(object):
|
||||
|
||||
with open(path, mode='w') as file_handle:
|
||||
file_handle.write(self.badge_svg_text)
|
||||
|
||||
|
||||
# Based on the following SO answer: https://stackoverflow.com/a/16008023/6252525
|
||||
def _get_approx_string_width(text, font_width, fixed_width=False):
|
||||
"""
|
||||
Get the approximate width of a string using a specific average font width.
|
||||
|
||||
Args:
|
||||
text(str): Text string to calculate width of.
|
||||
font_width(int): Average width of font characters.
|
||||
fixed_width(bool): Indicates that the font is fixed width.
|
||||
|
||||
Returns:
|
||||
int: Width of string in pixels.
|
||||
|
||||
Examples:
|
||||
|
||||
Call the function with a string and the maximum character width of the font you are using:
|
||||
|
||||
>>> int(_get_approx_string_width('hello', 10))
|
||||
29
|
||||
|
||||
This example shows the comparison of simplistic calculation based on a fixed width.
|
||||
Given a test string and a fixed font width of 10, we can calculate the width
|
||||
by multiplying the length and the font character with:
|
||||
|
||||
>>> test_string = 'GOOGLE|ijkl'
|
||||
>>> _get_approx_string_width(test_string, 10, fixed_width=True)
|
||||
110
|
||||
|
||||
Since some characters in the string are thinner than others we expect that the
|
||||
apporximate text width will be narrower than the fixed width calculation:
|
||||
|
||||
>>> _get_approx_string_width(test_string, 10)
|
||||
77
|
||||
|
||||
"""
|
||||
if fixed_width:
|
||||
return len(text) * font_width
|
||||
|
||||
size = 0.0
|
||||
|
||||
# A dictionary containing percentages that relate to how wide
|
||||
# each character will be represented in a variable width font.
|
||||
# These percentages can be calculated using the ``_get_character_percentage_dict`` function.
|
||||
char_width_percentages = {
|
||||
"lij|' ": 40.0,
|
||||
'![]fI.,:;/\\t': 50.0,
|
||||
'`-(){}r"': 60.0,
|
||||
'*^zcsJkvxy': 70.0,
|
||||
'aebdhnopqug#$L+<>=?_~FZT0123456789': 70.0,
|
||||
'BSPEAKVXY&UwNRCHD': 70.0,
|
||||
'QGOMm%W@': 100.0
|
||||
}
|
||||
|
||||
for s in text:
|
||||
percentage = 100.0
|
||||
for k in char_width_percentages.keys():
|
||||
if s in k:
|
||||
percentage = char_width_percentages[k]
|
||||
break
|
||||
size += (percentage / 100.0) * float(font_width)
|
||||
|
||||
return int(size)
|
||||
|
||||
# This is a helper function that can be used to generate alternate dictionaries
|
||||
# for the _get_approx_string_width function. The function is not needed for
|
||||
# normal operation of this package, and since it depends on the PIL package,
|
||||
# which is not included in the dependencies the function will remain commented out.
|
||||
#
|
||||
# def _get_character_percentage_dict(font_path, font_size):
|
||||
# """Get the dictionary used to estimate variable width font text lengths.
|
||||
#
|
||||
# Args:
|
||||
# font_path(str): Path to valid font file.
|
||||
# font_size(int): Font size to use.
|
||||
#
|
||||
# Returns: dict
|
||||
#
|
||||
# This function can be used to calculate the dictionary used in the
|
||||
# ``get_approx_string_width`` function.
|
||||
#
|
||||
# Examples:
|
||||
# >>> _get_character_percentage_dict('/Library/Fonts/Verdana.ttf', 9) # doctest: +ELLIPSIS
|
||||
# {"lij|' ": 40, '![]fI.,:;/\\\\t': 50, '`-(){}r"': 60, '*^zcsJkvxy': 70, ...
|
||||
# """
|
||||
# from PIL import ImageFont
|
||||
#
|
||||
# # List of groups in size order, smallest to largest
|
||||
# char_width_groups = [
|
||||
# "lij|' ",
|
||||
# '![]fI.,:;/\\t',
|
||||
# '`-(){}r"',
|
||||
# '*^zcsJkvxy',
|
||||
# 'aebdhnopqug#$L+<>=?_~FZT' + digits,
|
||||
# 'BSPEAKVXY&UwNRCHD',
|
||||
# 'QGOMm%W@',
|
||||
# ]
|
||||
#
|
||||
# def get_largest_in_group(group):
|
||||
# """Get the widest character from the group."""
|
||||
# return max([ImageFont.truetype(font_path, font_size).getsize(c)[0] for c in group])
|
||||
#
|
||||
# largest = char_width_groups[-1]
|
||||
# font_width = get_largest_in_group(largest)
|
||||
# return {group: int((get_largest_in_group(group) / font_width) * 100)
|
||||
# for group in char_width_groups}
|
||||
|
||||
|
||||
def parse_args(args):
|
||||
"""Parse the command line arguments."""
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=textwrap.dedent('''\
|
||||
Command line utility to generate .svg badges.
|
||||
|
||||
This utility can be used to generate .svg badge images, using configurable
|
||||
thresholds for coloring. Values can be passed as string, integer or floating
|
||||
point. The type will be detected automatically.
|
||||
|
||||
Running the utility with a --file option will result in the .svg image being
|
||||
written to file. Without the --file option the .svg file content will be
|
||||
written to stdout, so can be redirected to a file.
|
||||
|
||||
Some thresholds have been built in to save time. To use these thresholds you
|
||||
can simply specify the template name instead of threshold value/color pairs.
|
||||
|
||||
examples:
|
||||
|
||||
Here are some usage specific examples that may save time on defining
|
||||
thresholds.
|
||||
|
||||
Pylint
|
||||
anybadge.py --value=2.22 --file=pylint.svg pylint
|
||||
|
||||
anybadge.py --label=pylint --value=2.22 --file=pylint.svg \\
|
||||
2=red 4=orange 8=yellow 10=green
|
||||
|
||||
Coverage
|
||||
anybadge.py --value=65 --file=coverage.svg coverage
|
||||
|
||||
anybadge.py --label=coverage --value=65 --suffix='%%' --file=coverage.svg \\
|
||||
50=red 60=orange 80=yellow 100=green
|
||||
|
||||
CI Pipeline
|
||||
anybadge.py --label=pipeline --value=passing --file=pipeline.svg \\
|
||||
passing=green failing=red
|
||||
|
||||
'''))
|
||||
parser.add_argument('-l', '--label', type=str, help='The badge label.')
|
||||
parser.add_argument('-v', '--value', type=str, help='The badge value.', required=True)
|
||||
parser.add_argument('-m', '--value-format', type=str, default=None,
|
||||
help='Formatting string for value (e.g. "%%.2f" for 2dp floats)')
|
||||
parser.add_argument('-c', '--color', type=str, help='For fixed color badges use --color'
|
||||
'to specify the badge color.',
|
||||
default=DEFAULT_COLOR)
|
||||
parser.add_argument('-p', '--prefix', type=str, help='Optional prefix for value.',
|
||||
default='')
|
||||
parser.add_argument('-s', '--suffix', type=str, help='Optional suffix for value.',
|
||||
default='')
|
||||
parser.add_argument('-d', '--padding', type=int, help='Number of characters to pad on '
|
||||
'either side of the badge text.',
|
||||
default=NUM_PADDING_CHARS)
|
||||
parser.add_argument('-lp', '--label-padding', type=int, help='Number of characters to pad on '
|
||||
'either side of the badge label.', default=None)
|
||||
parser.add_argument('-vp', '--value-padding', type=int, help='Number of characters to pad on '
|
||||
'either side of the badge value.', default=None)
|
||||
parser.add_argument('-n', '--font', type=str,
|
||||
help='Font name. Supported fonts: '
|
||||
','.join(['"%s"' % x for x in FONT_WIDTHS.keys()]),
|
||||
default=DEFAULT_FONT)
|
||||
parser.add_argument('-z', '--font-size', type=int, help='Font size.',
|
||||
default=DEFAULT_FONT_SIZE)
|
||||
parser.add_argument('-t', '--template', type=str, help='Location of alternative '
|
||||
'template .svg file.',
|
||||
default=TEMPLATE_SVG)
|
||||
parser.add_argument('-st', '--style', type=str, help='Alternative style of badge to create. Valid '
|
||||
'values are "gitlab-scoped", "default". This '
|
||||
'overrides any templates passed using --template.')
|
||||
parser.add_argument('-u', '--use-max', action='store_true',
|
||||
help='Use the maximum threshold color when the value exceeds the '
|
||||
'maximum threshold.')
|
||||
parser.add_argument('-f', '--file', type=str, help='Output file location.')
|
||||
parser.add_argument('-o', '--overwrite', action='store_true',
|
||||
help='Overwrite output file if it already exists.')
|
||||
parser.add_argument('-r', '--text-color', type=str, help='Text color. Single value affects both label'
|
||||
'and value colors. A comma separated pair '
|
||||
'affects label and value text respectively.',
|
||||
default=DEFAULT_TEXT_COLOR)
|
||||
parser.add_argument('-e', '--semver', action='store_true', default=False,
|
||||
help='Treat value and thresholds as semantic versions.')
|
||||
parser.add_argument('args', nargs=argparse.REMAINDER, help='Pairs of <upper>=<color>. '
|
||||
'For example 2=red 4=orange 6=yellow 8=good. '
|
||||
'Read this as "Less than 2 = red, less than 4 = orange...".')
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
"""Generate a badge based on command line arguments."""
|
||||
|
||||
# Args may be sent from command line of as args directly.
|
||||
if not args:
|
||||
args = sys.argv[1:]
|
||||
|
||||
# Parse command line arguments
|
||||
args = parse_args(args)
|
||||
|
||||
label = args.label
|
||||
threshold_text = args.args
|
||||
suffix = args.suffix
|
||||
|
||||
# Check whether thresholds were sent as one word, and is in the
|
||||
# list of templates. If so, swap in the template.
|
||||
if len(args.args) == 1 and args.args[0] in BADGE_TEMPLATES:
|
||||
template_name = args.args[0]
|
||||
template_dict = BADGE_TEMPLATES[template_name]
|
||||
threshold_text = template_dict['threshold'].split(' ')
|
||||
if not args.label:
|
||||
label = template_dict['label']
|
||||
if not args.suffix and 'suffix' in template_dict:
|
||||
suffix = template_dict['suffix']
|
||||
|
||||
if not label:
|
||||
raise ValueError('Label has not been set. Please use --label argument.')
|
||||
|
||||
# Create threshold list from args
|
||||
threshold_list = [x.split('=') for x in threshold_text]
|
||||
threshold_dict = {x[0]: x[1] for x in threshold_list}
|
||||
|
||||
# Create badge object
|
||||
badge = Badge(label, args.value, value_prefix=args.prefix, value_suffix=suffix,
|
||||
default_color=args.color, num_padding_chars=args.padding,
|
||||
num_label_padding_chars=args.label_padding, num_value_padding_chars=args.value_padding,
|
||||
font_name=args.font, font_size=args.font_size, template=args.template, style=args.style,
|
||||
use_max_when_value_exceeds=args.use_max, thresholds=threshold_dict,
|
||||
value_format=args.value_format, text_color=args.text_color, semver=args.semver)
|
||||
|
||||
if args.file:
|
||||
# Write badge SVG to file
|
||||
badge.write_badge(args.file, overwrite=args.overwrite)
|
||||
else:
|
||||
print(badge.badge_svg_text)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
146
anybadge/cli.py
Normal file
@@ -0,0 +1,146 @@
|
||||
import argparse
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from anybadge.styles import Style
|
||||
from anybadge.templates import get_template
|
||||
from . import config
|
||||
from .badge import Badge
|
||||
|
||||
|
||||
def parse_args(args):
|
||||
"""Parse the command line arguments."""
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=textwrap.dedent('''\
|
||||
Command line utility to generate .svg badges.
|
||||
|
||||
This utility can be used to generate .svg badge images, using configurable
|
||||
thresholds for coloring. Values can be passed as string, integer or floating
|
||||
point. The type will be detected automatically.
|
||||
|
||||
Running the utility with a --file option will result in the .svg image being
|
||||
written to file. Without the --file option the .svg file content will be
|
||||
written to stdout, so can be redirected to a file.
|
||||
|
||||
Some thresholds have been built in to save time. To use these thresholds you
|
||||
can simply specify the template name instead of threshold value/color pairs.
|
||||
|
||||
examples:
|
||||
|
||||
Here are some usage specific examples that may save time on defining
|
||||
thresholds.
|
||||
|
||||
Pylint
|
||||
anybadge.py --value=2.22 --file=pylint.svg pylint
|
||||
|
||||
anybadge.py --label=pylint --value=2.22 --file=pylint.svg \\
|
||||
2=red 4=orange 8=yellow 10=green
|
||||
|
||||
Coverage
|
||||
anybadge.py --value=65 --file=coverage.svg coverage
|
||||
|
||||
anybadge.py --label=coverage --value=65 --suffix='%%' --file=coverage.svg \\
|
||||
50=red 60=orange 80=yellow 100=green
|
||||
|
||||
CI Pipeline
|
||||
anybadge.py --label=pipeline --value=passing --file=pipeline.svg \\
|
||||
passing=green failing=red
|
||||
|
||||
'''))
|
||||
parser.add_argument('-l', '--label', type=str, help='The badge label.')
|
||||
parser.add_argument('-v', '--value', type=str, help='The badge value.', required=True)
|
||||
parser.add_argument('-m', '--value-format', type=str, default=None,
|
||||
help='Formatting string for value (e.g. "%%.2f" for 2dp floats)')
|
||||
parser.add_argument('-c', '--color', type=str, help='For fixed color badges use --color'
|
||||
'to specify the badge color.',
|
||||
default=config.DEFAULT_COLOR)
|
||||
parser.add_argument('-p', '--prefix', type=str, help='Optional prefix for value.',
|
||||
default='')
|
||||
parser.add_argument('-s', '--suffix', type=str, help='Optional suffix for value.',
|
||||
default='')
|
||||
parser.add_argument('-d', '--padding', type=int, help='Number of characters to pad on '
|
||||
'either side of the badge text.',
|
||||
default=config.NUM_PADDING_CHARS)
|
||||
parser.add_argument('-lp', '--label-padding', type=int, help='Number of characters to pad on '
|
||||
'either side of the badge label.', default=None)
|
||||
parser.add_argument('-vp', '--value-padding', type=int, help='Number of characters to pad on '
|
||||
'either side of the badge value.', default=None)
|
||||
parser.add_argument('-n', '--font', type=str,
|
||||
help='Font name. Supported fonts: '
|
||||
','.join(['"%s"' % x for x in config.FONT_WIDTHS.keys()]),
|
||||
default=config.DEFAULT_FONT)
|
||||
parser.add_argument('-z', '--font-size', type=int, help='Font size.',
|
||||
default=config.DEFAULT_FONT_SIZE)
|
||||
parser.add_argument('-t', '--template', type=str, help='Location of alternative '
|
||||
'template .svg file.',
|
||||
default=get_template('default'))
|
||||
parser.add_argument('-st', '--style', type=str, help='Alternative style of badge to create. Valid '
|
||||
'values are "gitlab-scoped", "default". This '
|
||||
'overrides any templates passed using --template.')
|
||||
parser.add_argument('-u', '--use-max', action='store_true',
|
||||
help='Use the maximum threshold color when the value exceeds the '
|
||||
'maximum threshold.')
|
||||
parser.add_argument('-f', '--file', type=str, help='Output file location.')
|
||||
parser.add_argument('-o', '--overwrite', action='store_true',
|
||||
help='Overwrite output file if it already exists.')
|
||||
parser.add_argument('-r', '--text-color', type=str, help='Text color. Single value affects both label'
|
||||
'and value colors. A comma separated pair '
|
||||
'affects label and value text respectively.',
|
||||
default=config.DEFAULT_TEXT_COLOR)
|
||||
parser.add_argument('-e', '--semver', action='store_true', default=False,
|
||||
help='Treat value and thresholds as semantic versions.')
|
||||
parser.add_argument('args', nargs=argparse.REMAINDER, help='Pairs of <upper>=<color>. '
|
||||
'For example 2=red 4=orange 6=yellow 8=good. '
|
||||
'Read this as "Less than 2 = red, less than 4 = orange...".')
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
"""Generate a badge based on command line arguments."""
|
||||
|
||||
# Args may be sent from command line of as args directly.
|
||||
if not args:
|
||||
args = sys.argv[1:]
|
||||
|
||||
# Parse command line arguments
|
||||
args = parse_args(args)
|
||||
|
||||
label = args.label
|
||||
threshold_text = args.args
|
||||
suffix = args.suffix
|
||||
|
||||
# Check whether thresholds were sent as one word, and is in the
|
||||
# list of available styles. If so, swap in the style.
|
||||
if len(args.args) == 1 and Style.exists(args.args[0].upper()):
|
||||
style_name = args.args[0].upper()
|
||||
style = Style[style_name]
|
||||
threshold_text = style.threshold.split(' ')
|
||||
if not args.label and style.label:
|
||||
label = style.label
|
||||
if not args.suffix and style.suffix:
|
||||
suffix = style.suffix
|
||||
|
||||
if not label:
|
||||
raise ValueError('Label has not been set. Please use --label argument.')
|
||||
|
||||
# Create threshold list from args
|
||||
threshold_list = [x.split('=') for x in threshold_text]
|
||||
threshold_dict = {x[0]: x[1] for x in threshold_list}
|
||||
|
||||
# Create badge object
|
||||
badge = Badge(label, args.value, value_prefix=args.prefix, value_suffix=suffix,
|
||||
default_color=args.color, num_padding_chars=args.padding,
|
||||
num_label_padding_chars=args.label_padding, num_value_padding_chars=args.value_padding,
|
||||
font_name=args.font, font_size=args.font_size, template=args.template, style=args.style,
|
||||
use_max_when_value_exceeds=args.use_max, thresholds=threshold_dict,
|
||||
value_format=args.value_format, text_color=args.text_color, semver=args.semver)
|
||||
|
||||
if args.file:
|
||||
# Write badge SVG to file
|
||||
badge.write_badge(args.file, overwrite=args.overwrite)
|
||||
else:
|
||||
print(badge.badge_svg_text)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
27
anybadge/colors.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Create a dictionary of colors to make selections
|
||||
# easier.
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Color(Enum):
|
||||
WHITE = '#FFFFFF'
|
||||
SILVER = '#C0C0C0'
|
||||
GRAY = '#808080'
|
||||
BLACK = '#000000'
|
||||
RED = '#e05d44'
|
||||
BRIGHT_RED = '#FF0000'
|
||||
MAROON = '#800000'
|
||||
OLIVE = '#808000'
|
||||
LIME = '#00FF00'
|
||||
BRIGHT_YELLOW = '#FFFF00'
|
||||
YELLOW = '#dfb317'
|
||||
GREEN = '#4c1'
|
||||
YELLOW_GREEN = '#a4a61d'
|
||||
AQUA = '#00FFFF'
|
||||
TEAL = '#008080'
|
||||
BLUE = '#0000FF'
|
||||
NAVY = '#000080'
|
||||
FUCHSIA = '#FF00FF'
|
||||
PURPLE = '#800080'
|
||||
ORANGE = '#fe7d37'
|
||||
LIGHT_GREY = '#9f9f9f'
|
||||
22
anybadge/config.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Set some defaults
|
||||
DEFAULT_FONT = 'DejaVu Sans,Verdana,Geneva,sans-serif'
|
||||
DEFAULT_FONT_SIZE = 11
|
||||
NUM_PADDING_CHARS = 0.5
|
||||
DEFAULT_COLOR = '#4c1'
|
||||
DEFAULT_TEXT_COLOR = '#fff'
|
||||
MASK_ID_PREFIX = 'anybadge_'
|
||||
|
||||
# Dictionary for looking up approx pixel widths of
|
||||
# supported fonts and font sizes.
|
||||
FONT_WIDTHS = {
|
||||
'DejaVu Sans,Verdana,Geneva,sans-serif': {
|
||||
10: 9,
|
||||
11: 10,
|
||||
12: 11,
|
||||
},
|
||||
'Arial, Helvetica, sans-serif': {
|
||||
11: 8,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
3
anybadge/exceptions.py
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
class UnknownBadgeTemplate(Exception):
|
||||
"""The badge template is unknown."""
|
||||
62
anybadge/helpers.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Based on the following SO answer: https://stackoverflow.com/a/16008023/6252525
|
||||
def _get_approx_string_width(text, font_width, fixed_width=False):
|
||||
"""
|
||||
Get the approximate width of a string using a specific average font width.
|
||||
|
||||
Args:
|
||||
text(str): Text string to calculate width of.
|
||||
font_width(int): Average width of font characters.
|
||||
fixed_width(bool): Indicates that the font is fixed width.
|
||||
|
||||
Returns:
|
||||
int: Width of string in pixels.
|
||||
|
||||
Examples:
|
||||
|
||||
Call the function with a string and the maximum character width of the font you are using:
|
||||
|
||||
>>> int(_get_approx_string_width('hello', 10))
|
||||
29
|
||||
|
||||
This example shows the comparison of simplistic calculation based on a fixed width.
|
||||
Given a test string and a fixed font width of 10, we can calculate the width
|
||||
by multiplying the length and the font character with:
|
||||
|
||||
>>> test_string = 'GOOGLE|ijkl'
|
||||
>>> _get_approx_string_width(test_string, 10, fixed_width=True)
|
||||
110
|
||||
|
||||
Since some characters in the string are thinner than others we expect that the
|
||||
apporximate text width will be narrower than the fixed width calculation:
|
||||
|
||||
>>> _get_approx_string_width(test_string, 10)
|
||||
77
|
||||
|
||||
"""
|
||||
if fixed_width:
|
||||
return len(text) * font_width
|
||||
|
||||
size = 0.0
|
||||
|
||||
# A dictionary containing percentages that relate to how wide
|
||||
# each character will be represented in a variable width font.
|
||||
# These percentages can be calculated using the ``_get_character_percentage_dict`` function.
|
||||
char_width_percentages = {
|
||||
"lij|' ": 40.0,
|
||||
'![]fI.,:;/\\t': 50.0,
|
||||
'`-(){}r"': 60.0,
|
||||
'*^zcsJkvxy': 70.0,
|
||||
'aebdhnopqug#$L+<>=?_~FZT0123456789': 70.0,
|
||||
'BSPEAKVXY&UwNRCHD': 70.0,
|
||||
'QGOMm%W@': 100.0
|
||||
}
|
||||
|
||||
for s in text:
|
||||
percentage = 100.0
|
||||
for k in char_width_percentages.keys():
|
||||
if s in k:
|
||||
percentage = char_width_percentages[k]
|
||||
break
|
||||
size += (percentage / 100.0) * float(font_width)
|
||||
|
||||
return int(size)
|
||||
75
anybadge/server/cli.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import argparse
|
||||
import logging
|
||||
from http.server import HTTPServer
|
||||
from os import environ
|
||||
|
||||
from anybadge.server.request_handler import AnyBadgeHTTPRequestHandler
|
||||
from anybadge.server import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run(listen_address: str = None, port: int = None):
|
||||
if not listen_address:
|
||||
listen_address = config.DEFAULT_SERVER_LISTEN_ADDRESS
|
||||
|
||||
if not port:
|
||||
port = config.DEFAULT_SERVER_PORT
|
||||
|
||||
server_address = (listen_address, port)
|
||||
|
||||
global SERVER_PORT, SERVER_LISTEN_ADDRESS
|
||||
|
||||
SERVER_PORT = port
|
||||
SERVER_LISTEN_ADDRESS = listen_address
|
||||
|
||||
httpd = HTTPServer(server_address, AnyBadgeHTTPRequestHandler)
|
||||
logger.info('Serving at: http://%s:%s' % server_address)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
def parse_args():
|
||||
logger.debug('Parsing command line arguments.')
|
||||
parser = argparse.ArgumentParser(description="Run an anybadge server.")
|
||||
parser.add_argument('-p', '--port', type=int, default=DEFAULT_SERVER_PORT,
|
||||
help="Server port number. Default is %s. This can also be set via an environment "
|
||||
"variable called ``ANYBADGE_PORT``." % DEFAULT_SERVER_PORT)
|
||||
parser.add_argument('-l', '--listen-address', type=str, default=DEFAULT_SERVER_LISTEN_ADDRESS,
|
||||
help="Server listen address. Default is %s. This can also be set via an environment "
|
||||
"variable called ``ANYBADGE_LISTEN_ADDRESS``." % DEFAULT_SERVER_LISTEN_ADDRESS)
|
||||
parser.add_argument('-d', '--debug', action='store_true', help='Enable debug logging.')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""Run server."""
|
||||
|
||||
global DEFAULT_SERVER_PORT, DEFAULT_SERVER_LISTEN_ADDRESS, DEFAULT_LOGGING_LEVEL
|
||||
|
||||
# Check for environment variables
|
||||
if 'ANYBADGE_PORT' in environ:
|
||||
DEFAULT_SERVER_PORT = environ['ANYBADGE_PORT']
|
||||
|
||||
if 'ANYBADGE_LISTEN_ADDRESS' in environ:
|
||||
DEFAULT_SERVER_LISTEN_ADDRESS = environ['ANYBADGE_LISTEN_ADDRESS']
|
||||
|
||||
if 'ANYBADGE_LOG_LEVEL' in environ:
|
||||
DEFAULT_LOGGING_LEVEL = logging.getLevelName(environ['ANYBADGE_LOG_LEVEL'])
|
||||
|
||||
# Parse command line args
|
||||
args = parse_args()
|
||||
|
||||
# Set logging level
|
||||
logging_level = DEFAULT_LOGGING_LEVEL
|
||||
if args.debug:
|
||||
logging_level = logging.DEBUG
|
||||
|
||||
logging.basicConfig(format='%(asctime)-15s %(levelname)s:%(filename)s(%(lineno)d):%(funcName)s: %(message)s',
|
||||
level=logging_level)
|
||||
logger.info('Starting up anybadge server.')
|
||||
|
||||
run(listen_address=args.listen_address, port=args.port)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
9
anybadge/server/config.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import logging
|
||||
|
||||
|
||||
DEFAULT_SERVER_PORT = 8000
|
||||
DEFAULT_SERVER_LISTEN_ADDRESS = 'localhost'
|
||||
DEFAULT_LOGGING_LEVEL = logging.INFO
|
||||
|
||||
SERVER_PORT = DEFAULT_SERVER_PORT
|
||||
SERVER_LISTEN_ADDRESS = DEFAULT_SERVER_LISTEN_ADDRESS
|
||||
74
anybadge/server/request_handler.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import logging
|
||||
import urllib.parse as urlparse
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
|
||||
from anybadge import Badge
|
||||
from anybadge.server import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnyBadgeHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
"""Request handler for anybadge HTTP server."""
|
||||
|
||||
def do_HEAD(self):
|
||||
logging.debug('Sending head.')
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
logging.debug('Handling get request.')
|
||||
self.do_HEAD()
|
||||
|
||||
# Ignore request for favicon
|
||||
if self.path == '/favicon.ico':
|
||||
logging.debug('Ignoring favicon request.')
|
||||
return
|
||||
|
||||
# Parse the URL query string
|
||||
parsed = urlparse.urlparse(self.path)
|
||||
url_query = urlparse.parse_qs(parsed.query)
|
||||
|
||||
label = ''
|
||||
value = ''
|
||||
color = 'green'
|
||||
|
||||
# Extract the label and value portions
|
||||
if 'label' in url_query:
|
||||
label = url_query['label'][0]
|
||||
|
||||
if 'value' in url_query:
|
||||
value = url_query['value'][0]
|
||||
|
||||
logging.debug('Label: %s Value: %s', label, value)
|
||||
|
||||
if label and value and color:
|
||||
logging.debug('All parameters present.')
|
||||
badge = Badge(label=label, value=value, default_color=color)
|
||||
for line in badge.badge_svg_text:
|
||||
self.wfile.write(str.encode(line))
|
||||
|
||||
else:
|
||||
logging.debug('Not all parameters present.')
|
||||
|
||||
self.wfile.write(b"<html><head><title>Anybadge Web Server.</title></head>")
|
||||
self.wfile.write(b"<body>")
|
||||
|
||||
help_text = """
|
||||
<h1>Welcome to the Anybadge Web Server.</h1>
|
||||
|
||||
You are seeing this message because you haven't passed all the query parameters
|
||||
to display a badge.
|
||||
|
||||
You need to pass at least a <b>label</b> and <b>value</b> parameter.
|
||||
|
||||
Here is an example:
|
||||
|
||||
<a href="http://localhost:{port}/?label=Project%20Awesomeness&value=110%">\
|
||||
http://localhost:{port}/?label=Project%20Awesomeness&value=110%</a>
|
||||
""".format(port=config.SERVER_PORT)
|
||||
|
||||
for line in help_text.splitlines():
|
||||
self.wfile.write(str.encode('<p>%s</p>' % line))
|
||||
self.wfile.write(b"</body></html>")
|
||||
25
anybadge/styles.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
# Define some templates that can be used for common badge types, saving
|
||||
# from having to provide thresholds and labels each time.
|
||||
class Style(Enum):
|
||||
"""A style that can be used for common badge types."""
|
||||
|
||||
PYLINT = ('default.svg', '2=red 4=orange 8=yellow 10=green', 'pylint')
|
||||
COVERAGE = ('default.svg', '50=red 60=orange 80=yellow 100=green', 'coverage', '%')
|
||||
|
||||
def __init__(self, template, threshold, label, suffix=None):
|
||||
self.template = template
|
||||
self.threshold = threshold
|
||||
self.label = label
|
||||
self.suffix = suffix
|
||||
|
||||
@classmethod
|
||||
def exists(cls, name: str):
|
||||
"""Test whether a style exists."""
|
||||
try:
|
||||
_ = cls[name]
|
||||
return True
|
||||
except KeyError:
|
||||
return False
|
||||
19
anybadge/templates/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""Templates package."""
|
||||
import pkgutil
|
||||
|
||||
from anybadge.exceptions import UnknownBadgeTemplate
|
||||
|
||||
|
||||
def get_template(name: str) -> str:
|
||||
"""Get a template by name.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> get_template('default') # doctest: +ELLIPSIS
|
||||
'<?xml version="1.0" encoding="UTF-8...
|
||||
|
||||
"""
|
||||
try:
|
||||
return pkgutil.get_data(__name__, name + ".svg").decode("utf-8")
|
||||
except FileNotFoundError as e:
|
||||
raise UnknownBadgeTemplate from e
|
||||
23
anybadge/templates/default.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="{{ mask id }}">
|
||||
<rect width="{{ badge width }}" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#{{ mask id }})">
|
||||
<path fill="#555" d="M0 0h{{ color split x }}v20H0z"/>
|
||||
<path fill="{{ color }}" d="M{{ color split x }} 0h{{ value width }}v20H{{ color split x }}z"/>
|
||||
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
|
||||
</g>
|
||||
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ label anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ label }}</text>
|
||||
<text x="{{ label anchor }}" y="14">{{ label }}</text>
|
||||
</g>
|
||||
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ value anchor shadow }}" y="15" fill="#010101" fill-opacity=".3">{{ value }}</text>
|
||||
<text x="{{ value anchor }}" y="14">{{ value }}</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
22
anybadge/templates/gitlab_scoped.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="{{ badge width }}" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="{{ mask id }}">
|
||||
<rect width="{{ badge width }}" height="20" rx="10" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#{{ mask id }})">
|
||||
<path fill="{{ color }}" d="M0 0h{{ badge width }}v20H0z"/>
|
||||
<path fill="#262626" d="M{{ color split x }} 2h{{ value box width }}v16H{{ color split x }}z"/>
|
||||
<path fill="#262626" d="M{{ arc start }},18 a1,1 0 0,0 0,-16"/>
|
||||
<path fill="url(#b)" d="M0 0h{{ badge width }}v20H0z"/>
|
||||
</g>
|
||||
<g fill="{{ label text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ label anchor }}" y="14">{{ label }}</text>
|
||||
</g>
|
||||
<g fill="{{ value text color }}" text-anchor="middle" font-family="{{ font name }}" font-size="{{ font size }}">
|
||||
<text x="{{ value anchor }}" y="14">{{ value }}</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,160 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
Anybadge Server
|
||||
|
||||
This package provides a server for anybadge.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
from os import environ
|
||||
import logging
|
||||
import argparse
|
||||
from anybadge import Badge
|
||||
|
||||
# Import the correct version of HTTP server
|
||||
try:
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
except ImportError:
|
||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
# Import the correct version of urlparse, depending on which version
|
||||
# of Python we are using
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
import urllib.parse as urlparse
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_SERVER_PORT = 8000
|
||||
DEFAULT_SERVER_LISTEN_ADDRESS = 'localhost'
|
||||
DEFAULT_LOGGING_LEVEL = logging.INFO
|
||||
|
||||
SERVER_PORT = DEFAULT_SERVER_PORT
|
||||
SERVER_LISTEN_ADDRESS = DEFAULT_SERVER_LISTEN_ADDRESS
|
||||
|
||||
|
||||
class AnybadgeHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
"""Request handler for Anybadge HTTP server."""
|
||||
|
||||
def do_HEAD(self):
|
||||
logging.debug('Sending head.')
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
logging.debug('Handling get request.')
|
||||
self.do_HEAD()
|
||||
|
||||
# Ignore request for favicon
|
||||
if self.path == '/favicon.ico':
|
||||
logging.debug('Ignoring favicon request.')
|
||||
return
|
||||
|
||||
# Parse the URL query string
|
||||
parsed = urlparse.urlparse(self.path)
|
||||
url_query = urlparse.parse_qs(parsed.query)
|
||||
|
||||
label = ''
|
||||
value = ''
|
||||
color = 'green'
|
||||
|
||||
# Extract the label and value portions
|
||||
if 'label' in url_query:
|
||||
label = url_query['label'][0]
|
||||
|
||||
if 'value' in url_query:
|
||||
value = url_query['value'][0]
|
||||
|
||||
logging.debug('Label: %s Value: %s', label, value)
|
||||
|
||||
if label and value and color:
|
||||
logging.debug('All parameters present.')
|
||||
badge = Badge(label=label, value=value, default_color=color)
|
||||
for line in badge.badge_svg_text:
|
||||
self.wfile.write(str.encode(line))
|
||||
|
||||
else:
|
||||
logging.debug('Not all parameters present.')
|
||||
|
||||
self.wfile.write(b"<html><head><title>Anybadge Web Server.</title></head>")
|
||||
self.wfile.write(b"<body>")
|
||||
|
||||
help_text = """
|
||||
<h1>Welcome to the Anybadge Web Server.</h1>
|
||||
|
||||
You are seeing this message because you haven't passed all the query parameters
|
||||
to display a badge.
|
||||
|
||||
You need to pass at least a <b>label</b> and <b>value</b> parameter.
|
||||
|
||||
Here is an example:
|
||||
|
||||
<a href="http://localhost:{port}/?label=Project%20Awesomeness&value=110%">\
|
||||
http://localhost:{port}/?label=Project%20Awesomeness&value=110%</a>
|
||||
""".format(port=SERVER_PORT)
|
||||
|
||||
for line in help_text.splitlines():
|
||||
self.wfile.write(str.encode('<p>%s</p>' % line))
|
||||
self.wfile.write(b"</body></html>")
|
||||
|
||||
|
||||
def run(listen_address=DEFAULT_SERVER_LISTEN_ADDRESS, port=DEFAULT_SERVER_PORT):
|
||||
server_address = (listen_address, port)
|
||||
|
||||
global SERVER_PORT, SERVER_LISTEN_ADDRESS
|
||||
|
||||
SERVER_PORT = port
|
||||
SERVER_LISTEN_ADDRESS = listen_address
|
||||
|
||||
httpd = HTTPServer(server_address, AnybadgeHTTPRequestHandler)
|
||||
logging.info('Serving at: http://%s:%s' % server_address)
|
||||
httpd.serve_forever()
|
||||
|
||||
|
||||
def parse_args():
|
||||
logger.debug('Parsing command line arguments.')
|
||||
parser = argparse.ArgumentParser(description="Run an anybadge server.")
|
||||
parser.add_argument('-p', '--port', type=int, default=DEFAULT_SERVER_PORT,
|
||||
help="Server port number. Default is %s. This can also be set via an environment "
|
||||
"variable called ``ANYBADGE_PORT``." % DEFAULT_SERVER_PORT)
|
||||
parser.add_argument('-l', '--listen-address', type=str, default=DEFAULT_SERVER_LISTEN_ADDRESS,
|
||||
help="Server listen address. Default is %s. This can also be set via an environment "
|
||||
"variable called ``ANYBADGE_LISTEN_ADDRESS``." % DEFAULT_SERVER_LISTEN_ADDRESS)
|
||||
parser.add_argument('-d', '--debug', action='store_true', help='Enable debug logging.')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""Run server."""
|
||||
|
||||
global DEFAULT_SERVER_PORT, DEFAULT_SERVER_LISTEN_ADDRESS, DEFAULT_LOGGING_LEVEL
|
||||
|
||||
# Check for environment variables
|
||||
if 'ANYBADGE_PORT' in environ:
|
||||
DEFAULT_SERVER_PORT = environ['ANYBADGE_PORT']
|
||||
|
||||
if 'ANYBADGE_LISTEN_ADDRESS' in environ:
|
||||
DEFAULT_SERVER_LISTEN_ADDRESS = environ['ANYBADGE_LISTEN_ADDRESS']
|
||||
|
||||
if 'ANYBADGE_LOG_LEVEL' in environ:
|
||||
DEFAULT_LOGGING_LEVEL = logging.getLevelName(environ['ANYBADGE_LOG_LEVEL'])
|
||||
|
||||
# Parse command line args
|
||||
args = parse_args()
|
||||
|
||||
# Set logging level
|
||||
logging_level = DEFAULT_LOGGING_LEVEL
|
||||
if args.debug:
|
||||
logging_level = logging.DEBUG
|
||||
|
||||
logging.basicConfig(format='%(asctime)-15s %(levelname)s:%(filename)s(%(lineno)d):%(funcName)s: %(message)s',
|
||||
level=logging_level)
|
||||
logger.info('Starting up anybadge server.')
|
||||
|
||||
run(listen_address=args.listen_address, port=args.port)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="79" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="118" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_1">
|
||||
<rect width="79" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_43">
|
||||
<rect width="118" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_1)">
|
||||
<g mask="url(#anybadge_43)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#00FFFF" d="M41 0h38v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h79v20H0z"/>
|
||||
<path fill="#00FFFF" d="M41 0h77v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h118v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="61.0" y="15" fill="#010101" fill-opacity=".3">aqua</text>
|
||||
<text x="60.0" y="14">aqua</text>
|
||||
<text x="80.5" y="15" fill="#010101" fill-opacity=".3">Color.AQUA</text>
|
||||
<text x="79.5" y="14">Color.AQUA</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="83" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="122" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_2">
|
||||
<rect width="83" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_44">
|
||||
<rect width="122" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_2)">
|
||||
<g mask="url(#anybadge_44)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#000000" d="M41 0h42v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h83v20H0z"/>
|
||||
<path fill="#000000" d="M41 0h81v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h122v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">black</text>
|
||||
<text x="62.0" y="14">black</text>
|
||||
<text x="82.5" y="15" fill="#010101" fill-opacity=".3">Color.BLACK</text>
|
||||
<text x="81.5" y="14">Color.BLACK</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="76" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_3">
|
||||
<rect width="76" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_45">
|
||||
<rect width="115" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_3)">
|
||||
<g mask="url(#anybadge_45)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#0000FF" d="M41 0h35v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h76v20H0z"/>
|
||||
<path fill="#0000FF" d="M41 0h74v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h115v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="59.5" y="15" fill="#010101" fill-opacity=".3">blue</text>
|
||||
<text x="58.5" y="14">blue</text>
|
||||
<text x="79.0" y="15" fill="#010101" fill-opacity=".3">Color.BLUE</text>
|
||||
<text x="78.0" y="14">Color.BLUE</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="134" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_6">
|
||||
<rect width="95" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_48">
|
||||
<rect width="134" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_6)">
|
||||
<g mask="url(#anybadge_48)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#FF00FF" d="M41 0h54v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h95v20H0z"/>
|
||||
<path fill="#FF00FF" d="M41 0h93v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h134v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="69.0" y="15" fill="#010101" fill-opacity=".3">fuchsia</text>
|
||||
<text x="68.0" y="14">fuchsia</text>
|
||||
<text x="88.5" y="15" fill="#010101" fill-opacity=".3">Color.FUCHSIA</text>
|
||||
<text x="87.5" y="14">Color.FUCHSIA</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="118" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_7">
|
||||
<rect width="78" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_49">
|
||||
<rect width="118" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_7)">
|
||||
<g mask="url(#anybadge_49)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#808080" d="M41 0h37v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h78v20H0z"/>
|
||||
<path fill="#808080" d="M41 0h77v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h118v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="60.5" y="15" fill="#010101" fill-opacity=".3">gray</text>
|
||||
<text x="59.5" y="14">gray</text>
|
||||
<text x="80.5" y="15" fill="#010101" fill-opacity=".3">Color.GRAY</text>
|
||||
<text x="79.5" y="14">Color.GRAY</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="85" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_8">
|
||||
<rect width="85" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_50">
|
||||
<rect width="125" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_8)">
|
||||
<g mask="url(#anybadge_50)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#4c1" d="M41 0h44v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h85v20H0z"/>
|
||||
<path fill="#4c1" d="M41 0h84v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h125v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="64.0" y="15" fill="#010101" fill-opacity=".3">green</text>
|
||||
<text x="63.0" y="14">green</text>
|
||||
<text x="84.0" y="15" fill="#010101" fill-opacity=".3">Color.GREEN</text>
|
||||
<text x="83.0" y="14">Color.GREEN</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="76" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_10">
|
||||
<rect width="76" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_52">
|
||||
<rect width="116" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_10)">
|
||||
<g mask="url(#anybadge_52)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#00FF00" d="M41 0h35v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h76v20H0z"/>
|
||||
<path fill="#00FF00" d="M41 0h75v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h116v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="59.5" y="15" fill="#010101" fill-opacity=".3">lime</text>
|
||||
<text x="58.5" y="14">lime</text>
|
||||
<text x="79.5" y="15" fill="#010101" fill-opacity=".3">Color.LIME</text>
|
||||
<text x="78.5" y="14">Color.LIME</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_11">
|
||||
<rect width="95" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_53">
|
||||
<rect width="138" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_11)">
|
||||
<g mask="url(#anybadge_53)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#800000" d="M41 0h54v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h95v20H0z"/>
|
||||
<path fill="#800000" d="M41 0h97v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h138v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="69.0" y="15" fill="#010101" fill-opacity=".3">maroon</text>
|
||||
<text x="68.0" y="14">maroon</text>
|
||||
<text x="90.5" y="15" fill="#010101" fill-opacity=".3">Color.MAROON</text>
|
||||
<text x="89.5" y="14">Color.MAROON</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="79" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_12">
|
||||
<rect width="79" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_54">
|
||||
<rect width="115" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_12)">
|
||||
<g mask="url(#anybadge_54)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#000080" d="M41 0h38v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h79v20H0z"/>
|
||||
<path fill="#000080" d="M41 0h74v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h115v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="61.0" y="15" fill="#010101" fill-opacity=".3">navy</text>
|
||||
<text x="60.0" y="14">navy</text>
|
||||
<text x="79.0" y="15" fill="#010101" fill-opacity=".3">Color.NAVY</text>
|
||||
<text x="78.0" y="14">Color.NAVY</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="123" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_13">
|
||||
<rect width="80" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_55">
|
||||
<rect width="123" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_13)">
|
||||
<g mask="url(#anybadge_55)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#808000" d="M41 0h39v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h80v20H0z"/>
|
||||
<path fill="#808000" d="M41 0h82v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h123v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="61.5" y="15" fill="#010101" fill-opacity=".3">olive</text>
|
||||
<text x="60.5" y="14">olive</text>
|
||||
<text x="83.0" y="15" fill="#010101" fill-opacity=".3">Color.OLIVE</text>
|
||||
<text x="82.0" y="14">Color.OLIVE</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="92" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_14">
|
||||
<rect width="92" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_56">
|
||||
<rect width="135" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_14)">
|
||||
<g mask="url(#anybadge_56)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#fe7d37" d="M41 0h51v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h92v20H0z"/>
|
||||
<path fill="#fe7d37" d="M41 0h94v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h135v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="67.5" y="15" fill="#010101" fill-opacity=".3">orange</text>
|
||||
<text x="66.5" y="14">orange</text>
|
||||
<text x="89.0" y="15" fill="#010101" fill-opacity=".3">Color.ORANGE</text>
|
||||
<text x="88.0" y="14">Color.ORANGE</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="89" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="129" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_15">
|
||||
<rect width="89" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_57">
|
||||
<rect width="129" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_15)">
|
||||
<g mask="url(#anybadge_57)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#800080" d="M41 0h48v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h89v20H0z"/>
|
||||
<path fill="#800080" d="M41 0h88v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h129v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="66.0" y="15" fill="#010101" fill-opacity=".3">purple</text>
|
||||
<text x="65.0" y="14">purple</text>
|
||||
<text x="86.0" y="15" fill="#010101" fill-opacity=".3">Color.PURPLE</text>
|
||||
<text x="85.0" y="14">Color.PURPLE</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="71" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="108" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_16">
|
||||
<rect width="71" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_58">
|
||||
<rect width="108" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_16)">
|
||||
<g mask="url(#anybadge_58)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#e05d44" d="M41 0h30v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h71v20H0z"/>
|
||||
<path fill="#e05d44" d="M41 0h67v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h108v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="57.0" y="15" fill="#010101" fill-opacity=".3">red</text>
|
||||
<text x="56.0" y="14">red</text>
|
||||
<text x="75.5" y="15" fill="#010101" fill-opacity=".3">Color.RED</text>
|
||||
<text x="74.5" y="14">Color.RED</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="86" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="127" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_17">
|
||||
<rect width="86" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_59">
|
||||
<rect width="127" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_17)">
|
||||
<g mask="url(#anybadge_59)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#C0C0C0" d="M41 0h45v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h86v20H0z"/>
|
||||
<path fill="#C0C0C0" d="M41 0h86v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h127v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="64.5" y="15" fill="#010101" fill-opacity=".3">silver</text>
|
||||
<text x="63.5" y="14">silver</text>
|
||||
<text x="85.0" y="15" fill="#010101" fill-opacity=".3">Color.SILVER</text>
|
||||
<text x="84.0" y="14">Color.SILVER</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="74" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_18">
|
||||
<rect width="74" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_60">
|
||||
<rect width="115" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_18)">
|
||||
<g mask="url(#anybadge_60)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#008080" d="M41 0h33v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h74v20H0z"/>
|
||||
<path fill="#008080" d="M41 0h74v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h115v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="58.5" y="15" fill="#010101" fill-opacity=".3">teal</text>
|
||||
<text x="57.5" y="14">teal</text>
|
||||
<text x="79.0" y="15" fill="#010101" fill-opacity=".3">Color.TEAL</text>
|
||||
<text x="78.0" y="14">Color.TEAL</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="81" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="123" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_19">
|
||||
<rect width="81" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_61">
|
||||
<rect width="123" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_19)">
|
||||
<g mask="url(#anybadge_61)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#FFFFFF" d="M41 0h40v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h81v20H0z"/>
|
||||
<path fill="#FFFFFF" d="M41 0h82v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h123v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="62.0" y="15" fill="#010101" fill-opacity=".3">white</text>
|
||||
<text x="61.0" y="14">white</text>
|
||||
<text x="83.0" y="15" fill="#010101" fill-opacity=".3">Color.WHITE</text>
|
||||
<text x="82.0" y="14">Color.WHITE</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="87" height="20">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<mask id="anybadge_20">
|
||||
<rect width="87" height="20" rx="3" fill="#fff"/>
|
||||
<mask id="anybadge_62">
|
||||
<rect width="135" height="20" rx="3" fill="#fff"/>
|
||||
</mask>
|
||||
<g mask="url(#anybadge_20)">
|
||||
<g mask="url(#anybadge_62)">
|
||||
<path fill="#555" d="M0 0h41v20H0z"/>
|
||||
<path fill="#dfb317" d="M41 0h46v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h87v20H0z"/>
|
||||
<path fill="#dfb317" d="M41 0h94v20H41z"/>
|
||||
<path fill="url(#b)" d="M0 0h135v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="21.5" y="15" fill="#010101" fill-opacity=".3">Color</text>
|
||||
<text x="20.5" y="14">Color</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="65.0" y="15" fill="#010101" fill-opacity=".3">yellow</text>
|
||||
<text x="64.0" y="14">yellow</text>
|
||||
<text x="89.0" y="15" fill="#010101" fill-opacity=".3">Color.YELLOW</text>
|
||||
<text x="88.0" y="14">Color.YELLOW</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
12
setup.py
@@ -20,18 +20,20 @@ setup(
|
||||
version=version,
|
||||
author='Jon Grace-Cox',
|
||||
author_email='jongracecox@gmail.com',
|
||||
py_modules=['anybadge', 'anybadge_server'],
|
||||
packages=['anybadge'],
|
||||
py_modules=['anybadge_server'],
|
||||
setup_requires=['setuptools', 'wheel'],
|
||||
tests_require=[],
|
||||
install_requires=['packaging'],
|
||||
data_files=[],
|
||||
package_data={'anybadge': ['templates/*.svg']},
|
||||
options={
|
||||
'bdist_wheel': {'universal': True}
|
||||
'bdist_wheel': {'universal': False}
|
||||
},
|
||||
python_requires='>=3.4',
|
||||
url='https://github.com/jongracecox/anybadge',
|
||||
entry_points={
|
||||
'console_scripts': ['anybadge=anybadge:main',
|
||||
'anybadge-server=anybadge_server:main'],
|
||||
'console_scripts': ['anybadge=anybadge.cli:main',
|
||||
'anybadge-server=anybadge.server.cli:main'],
|
||||
},
|
||||
classifiers=[
|
||||
'License :: OSI Approved :: MIT License'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from unittest import TestCase
|
||||
from anybadge import Badge, parse_args, main
|
||||
from anybadge import Badge
|
||||
from anybadge.cli import main, parse_args
|
||||
|
||||
|
||||
class TestAnybadge(TestCase):
|
||||
|
||||