=================== Additional Commands =================== .. |dash| unicode:: U+2014 .. EM DASH SIGN This section deals with some of the additional commands available in :doc:`protograf ` that are not covered in detail elsewhere. You should already be familiar with all of the :doc:`Basic Concepts `, and have created some basic scripts of your own using the :doc:`Core Shapes `. .. _table-of-contents-addcmd: - `Common Command`_ - `Default Command`_ - `Font Command`_ - `Extract Command`_ - `Random Command`_ - `Today Command`_ - `BGG Command`_ .. _the-common-command: Common Command ============== `↑ `_ The ``Common()`` command allows different shapes to use exactly the same properties. These properties are listed in much the same way as they would for an actual shape, but the ``Common()`` command does *not* actually draw anything itself. For example: .. code:: python colors = Common(stroke="tomato", fill="gold") Circle(common=colors, cx=1, cy=2) Square(common=colors, cx=2, cy=2) Here the circle and the square will both use the ``common`` properties that have been assigned to the ``colors`` name. .. NOTE:: The common properties **cannot** be overwritten by a shape; if set, they will be used and cannot be changed on a case-by-case basis! If you want to be able to do such overwrites, rather use the `Default Command`_. .. _the-default-command: Default Command =============== `↑ `_ The ``Default()`` command allows different shapes to potentially all share the same properties. These properties are listed in much the same way as they would for an actual shape, but the ``Default()`` command does *not* actually draw anything itself. .. NOTE:: Unlike the ``Common()`` command, the common properties set by the ``Default()`` command **will** be overwritten by any of the same ones which are set by the shape itself! For a shape to make use of properties set by a ``Default()`` command, the shape must assign the name given to the ``Default()`` to its *default* property. For example: .. |dfc| image:: images/custom/commands/defaults.png :width: 330 ===== ====== |dfc| This example shows setting and using defaults for shapes. .. code:: python def1 = Default( radius=0.75, fill="gold", stroke_width=4) def2 = Default( label="ne", label_size=24, stroke_width=2) Circle( default=def2, cx=1, cy=1, label_size=12) Circle( default=def1, cx=2, cy=2.5, fill="aqua") Circle( default=[def1, def2], cx=3, cy=5, radius=1) Here the various circles make use of the ``default`` properties that have been assigned to the ``def1`` and ``def2`` names. The top circle makes use of properties assigned to ``def2`` to set the properties of *radius*, *fill* and *stroke_width*. The middle circle makes use of properties assigned to ``def1``. However, the *fill* color is set to ``aqua`` and not ``gold`` because the property value set in the shape itself overrides the one supplied in ``def1``. The bottom circle makes use of properties assigned to **both** of the ``def1`` and ``def2`` names. It does this by using them in a list format, as shown by the square brackets from ``[`` to ``]``. Note that the **order**, when using multiple Defaults, is important. Its clear in the bottom circle that ``stroke_width=2`` from ``def2`` supercedes ``stroke_width=4`` from ``def1`` i.e. the values from the second, and onwards, Defaults override those from earlier ones. ===== ====== .. _the-font-command: Font Command ============ `↑ `_ .. NOTE:: There is a section with much more in-depth discussion on the :doc:`use of fonts ` which could be helpful, especially if you have problems... The ``Font()`` command will change the default font in use from that point forward in the script. Usually, the font's properties will be set at the time that a ``Text()`` command is issued, and multiple ``Text()`` commands can obviously use the `Common Command`_ to avoid duplication, but it can sometimes be useful to set the font properties more widely, especially where locating or setting up the font The following properties are available for the command: - *name* - the name of the font face - *size* - the font "height" in points - *stroke* - the font color; either a built-in color or a hexadecimal value If you need to use a specific style name that differs from the default style, for example *italic* or *bold*, you will need to refer to that font by its name, for example ``Arial-Bold``. Because the *name* is compulsory, its often omitted from the command. Example 1. Common Fonts ----------------------- .. |fc1| image:: images/custom/commands/fonts.png :width: 330 ===== ====== |fc1| This example shows the use of the command with different properties: .. code:: python Font("Helvetica", size=9, stroke="gold") Text(text="Helvetica 9pt gold", x=0, y=1, align="left") Font("Courier", size=10, stroke="cyan") Text(text="Courier 10pt aqua", x=0, y=2, align="left") Font("Times-Roman", size=11, stroke="tomato") Text(text="Times-Roman 11pt red", x=0, y=3, align="left") Font("Arial", size=12, stroke="black") Text(text="Arial 12pt black", x=0, y=4, align="left") The first three are examples of the :ref:`standard fonts ` available to a PDF. Standard fonts can be safely used anywhere that :doc:`protograf ` can be run. The fourth font is a *custom* one that would need to first be installed onto the machine where the script is to be run. .. IMPORTANT:: The first time that *any* custom font is used, in any of your scripts, will trigger a "discovery" process whereby *all* fonts are catalogued. This will cause a long delay |dash| however, after that the catalogue details are stored in a single file. This can be used directly and quickly, so after that your scripts will not have the delay. In each example, the name of the font appears first, followed by any further details as to its properties. ===== ====== .. _the-extract-command: Extract Command =============== `↑ `_ The ``Extract()`` command allows portions of pages to be extracted as PNG images. While it is possible to do this with third-party apps or tools, any manual approach can be a bit tiresome if it needs to be continually repeated! Specifying which page, or pages, are affected, along with the portions required, will automatically create these images after the final PDF is produced by **protograf**. There are a number of properties that can be assigned to an ``Extract``. The first must always be the page, or pages, where the extraction must happen. These can be specified as text e.g.: ``"42"``, ``"2-4"``, or ``"3-5,7,9"``, or in a list form e.g.: ``[42]``, ``[2, 3, 4]``, or ``[3, 4, 5, 7, 9]``. The following properties can be used to specify what part of the page must be extracted: - *cols_rows*: two numbers - either comma-separated in text **or** in a list e.g. ``"3,4"`` or ``[5, 6]``. The first number is how many columns the page should be divided into and the second number is how many rows the page should be divided into. So a `"2,2"`` value divides the page into quarters. - *areas* - this is a list of sets of numbers, with four numbers in each set. The set numbers represent the top-left *x* and *y* and the bottom-right *x* and *y* locations on the page of a rectangle that must be extracted e.g. ``[(1, 1, 5, 5), (2, 2, 6, 7)]`` will extract two rectangular images |dash| the first being 4cm in height and width and starting 1cm away from the top and left of the page, and the second being 4cm width by 5cm in height and starting 2cm away from the top and left of the page - *width* and *height* - together with *x* and *y* these specify a rectangular area of a page; when used with ``repeat=True`` this area will be repeatedly extracted for as many times as the area fits into the page in terms of its width across and height down. .. NOTE:: Areas are **always** relative to the edges of the page; they do **not** make use of any page margins. Similarly *cols_rows* always divide up the whole page, ignoring any page margins. The *x* and *y* values also ignore any page margins .. WARNING:: Do **NOT** use different extraction types in the same command! A *cols_rows* example: .. code:: python Extract("1-3" , cols_rows='2,2') An *areas* example: .. code:: python Extract("2,4,6", areas=[(1, 1, 5, 5), (2, 2, 6, 7)]) A *width* and *height* example: .. code:: python Extract("2,4-6", width=6.5, height=9, x=1.25, y=1.25, repeat=True) By default, the images are named after the PDF file being created, along with a ``-N`` suffix, where N is the page number, and then a ``-M`` where M is the sequence number of the image. For example, the fourth image on the third page extracted from a file called ``demo.pdf`` would be called ``demo-3-4.png``. There is also an optional *names* property that can be used to specify names for the images. This is just a list of text values. If the list is not long enough for all the images, the naming will revert back to the default approach. For example: .. code:: python Extract( "1-3" , cols_rows='2,2', names=[ 'top-left-quarter', 'bottom-left-quarter', 'top-right-quarter', 'bottom-right-quarter', ] ) This extract will create images named ``top-left-quarter.png``, ``bottom-left-quarter.png``, ``top-right-quarter.png``, and ``bottom-right-quarter.png``. The extract order for *cols_rows* will process each column in turn, followed by all the rows in that column. .. HINT:: It is possible to have multiple ``Extract()`` commands in a script; so different *areas* and/or *cols_rows* and/or *width*/*height* rectangles can be extracted from the same page or pages. .. _random-command: Random Command ============== `↑ `_ The ``Random()`` command allows the generation of numbers within a range. .. code:: python Random() will randomly generate a decimal number in the range to 0 to 1, rounded to 2 decimal places. .. code:: python Random(10) will randomly generate a decimal number in the range to 1 to 10, rounded to 2 decimal places. .. code:: python Random(10, 20, 3) will randomly generate a number in the range to 20 to 30, rounded to 3 decimal places. The second number is added to the range of random numbers generated in the range to 1 to 10 .. _the-today-command: Today Command ============= `↑ `_ The ``Today()`` command will insert text with the current date and/or time. Varying date formats can be used. Two properties are available: - *details* - this can be one of: ``date``, ``datetime``, ``time``, ``year``, ``month`` or ``day```. - *style* - this can be ``usa`` or ``eur``; if not provided then ``ISO`` date style is used |dash| see the ``"1."`` example below. Example 1. Common Usage ----------------------- `^ `_ .. |df1| image:: images/customised/dates_formats.png :width: 330 ===== ====== |df1| This example shows the use of the command with different *details* and *style* - the first example just uses defaults. .. code:: python dtext = Common(x=0.25, align="left", font_size=8) Text( common=dtext, y=1, text="1. "+Today()) Text( common=dtext, y=2, text="2. "+Today(details="date", style="usa")) Text( common=dtext, y=3, text="3. "+Today(details="date", style="eur")) Text( common=dtext, y=4, text="4. "+Today(details="datetime", style="usa")) Text( common=dtext, y=5, stroke=red, text="5. "+Today(details="datetime", style="eur")) Because the output of the ``Today()`` command is provided as text, it can be used for the *text* property of the ``Text()`` command and located and styled as part of that command. ===== ====== .. _the-bgg-command: BGG Command =========== `↑ `_ The `BGG()` command is designed to retrieve board game data from the `BoardGameGeek (BGG) `_ database, via its API, and provide that data in a form suitable for use in :doc:`Card Decks `. Examples of using BGG API data for card creation can be found at :ref:`BGG Examples `. .. IMPORTANT:: 1. Note that access to the BGG API can only be used in terms of its license: https://boardgamegeek.com/wiki/page/XML_API_Terms_of_Use 2. Access to the BGG API requires that you apply to the site and request permission; when granted, this will allow you to generate a *token* |dash| a long, unique, set of characters and numbers that look something like ``48398139-7fb1-4809-b7af-0be28d35dcec`` |dash| for details see https://boardgamegeek.com/application/ 3. Furthermore, there is an upper limit to how many games can be retrieved at a time |dash| best available knowledge suggests this is about 15,000. 4. Copies are kept of data downloaded from BGG |dash| and this data will *not* be re-retrieved from BGG unless you delete those copies; see `Caching`_ below. Usage ----- The ``BGG`` command allows game data to be retrieved either by providing: - a comma-delimited list |dash| in square brackets from ``[`` to ``]`` |dash| of game IDs - the ID of a BoardGameGeek user; by default all games in that user's collection will be retrieved, unless filters are used In both cases, you will also need to supply your **BGG API token**. The ID of a game appears in its URL; so, for example, the ID for the boardgame "Monopoly" is ``1406`` and is found at https://boardgamegeek.com/boardgame/1406 Example 1. Games by ID ~~~~~~~~~~~~~~~~~~~~~~ To retrieve games which have known IDs, create a list of those numbers, using square brackets; for example, for ID's 1, 2 and 3: : .. code:: python BGG(token="ABC123", ids=[1, 2, 3]) When the command runs, you will get the following feedback:: FEEDBACK:: All board game data accessed via this tool is owned by BoardGameGeek and provided through their XML API If you add the *progress* property, you will get feedback on the process as it runs, showing that each game is being processed i.e. .. code:: python BGG(token="ABC123", ids=[1, 2, 3], progress=True) shows:: FEEDBACK:: Retrieving game '1' from BoardGameGeek... FEEDBACK:: Retrieving game '2' from BoardGameGeek... FEEDBACK:: Retrieving game '3' from BoardGameGeek... Example 2. Games for a BGG User ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To retrieve games for an (imaginary) user with the username ``BenKenobi1976``: .. code:: python BGG(token="ABC123", user='BenKenobi1976') A collection can be very large; you may want to filter it to create a `Subset of Games`_. .. _protograf_caching: Caching ------- *Caching* is the process of storing a copy of something |dash| usually a file, an image or some other kind of data |dash| so that it does not have to be retrieved again. This is useful when accessing resources from the internet, as it saves time and bandwidth. In the case of BoardGameGeek data, copies of the game data and images are stored under your user directory on your local machine (or where ever you are running :doc:`protograf `). The caching directory is called ``.protograf`` and this will have a ``bgg`` subdirectory where game data |dash| in the form of ``.pck`` files and ``images`` and ``thumbs`` |dash| are stored. If you delete these folders and files, they will be recreated the next time your script runs i.e. all of their data will need to be downloaded again. Output Fields ------------- The results of a successful command, that returns one or more games, includes data for the following fields: - **AVERAGEWEIGHT** ~ - **BAYESAVERAGE** ~ - **BGG** ~ - **CATEGORIES** ~ - **DESCRIPTION** ~ - **DESCRIPTION_SHORT** ~ - **DESIGNERS** ~ - **DISPLAY** ~ - **EXPANDS** ~ - **EXPANSION** ~ - **EXPANSIONS** ~ - **FAMILIES** ~ - **ID** ~ - **IMAGE** ~ - **IMPLEMENTATIONS** ~ - **MAXPLAYERS** ~ - **MECHANICS** ~ - **MEDIAN** ~ - **MINAGE** ~ - **MINPLAYERS** ~ - **NAME** ~ - **NUMCOMMENTS** ~ - **NUMWEIGHTS** ~ - **OWNED** ~ - **PLAYERS** ~ - **PLAYINGTIME** ~ - **PROPERTIES** ~ - **PUBLISHERS** ~ - **RANKS** ~ - **SHORT** ~ - **STDDEV** ~ - **THUMBNAIL** ~ - **TRADING** ~ - **USERSRATED** ~ - **WANTING** ~ - **WISHING** ~ - **YEARPUBLISHED** ~ .. HINT:: This program's developer was not able to find an authoritative set of descriptions for these fields; but they do seem mostly self-obvious, assuming you have made use of BoardGameGeek's database to manage your game collection. If you retrieve data for a user's collection, there will also be an additional set of fields, with data specific to that user: - **USER_GAME** ~ - **USER_OWN** ~ - **USER_PREORDERED** ~ - **USER_PREVOWNED** ~ - **USER_RATING** ~ - **USER_WANT** ~ - **USER_WANTTOBUY** ~ - **USER_WANTTOPLAY** ~ - **USER_WISHLIST** ~ - **USER_WISHLISTPRIORITY** ~ Subset of Games --------------- You can retrieve a subset of games for a user by providing one or more items to filter their collection on. These are added as extra properties to the ``BGG()`` command. For example: .. code:: python bgames = BGG( token="ABC123", user="BenKenobi1976", want_to_play=True, own=True, ) In this example, games must be marked both as "want to play" items **and** items that are "own"ed in the collection of the (totally imaginary) user called ``BenKenobi1976``. .. HINT:: A user's entire collection is retrieved at once |dash| so there is *no* "progress" option available! The full list of property filters that can be used, when accessing a user's collection, are: - *own* - include (if ``True``) or exclude (if ``False``) owned items - *rated* - include (if ``True``) or exclude (if ``False``) rated items - *played* - include (if ``True``) or exclude (if ``False``) played items - *commented* - include (if ``True``) or exclude (if ``False``) items commented on - *trade* - include (if ``True``) or exclude (if ``False``) items for trade - *want* - include (if ``True``) or exclude (if ``False``) items wanted in trade - *wishlist* - include (if ``True``) or exclude (if ``False``) items in the wishlist - *preordered* - include (if ``True``) or exclude (if ``False``) preordered items - *want_to_play* - include (if ``True``) or exclude (if ``False``) items wanting to play - *want_to_buy* - include (if ``True``) or exclude (if ``False``) items wanting to buy - *prev_owned* - include (if ``True``) or exclude (if ``False``) previously owned items - *has_parts* - include (if ``True``) or exclude (if ``False``) items for which there is a comment in the "Has parts" field - *want_parts* - include (if ``True``) or exclude (if ``False``) items for which there is a comment in the "Want parts" field