pax_global_header00006660000000000000000000000064144566760270014533gustar00rootroot0000000000000052 comment=11868e2696cc0afcd0425abd039ebf009d2828a7 alanhamlett-readtime-11868e2/000077500000000000000000000000001445667602700160755ustar00rootroot00000000000000alanhamlett-readtime-11868e2/.coveragerc000066400000000000000000000001611445667602700202140ustar00rootroot00000000000000[run] branch = true omit = readtime/__about__.py [report] omit = readtime/__about__.py */python?.?/* alanhamlett-readtime-11868e2/.github/000077500000000000000000000000001445667602700174355ustar00rootroot00000000000000alanhamlett-readtime-11868e2/.github/workflows/000077500000000000000000000000001445667602700214725ustar00rootroot00000000000000alanhamlett-readtime-11868e2/.github/workflows/tests.yml000066400000000000000000000015061445667602700233610ustar00rootroot00000000000000name: Tests on: pull_request: push: branches: - master jobs: tests: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11'] name: Run Tests with Python ${{ matrix.python-version }} steps: - name: Checkout uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} architecture: x64 - name: Install Dependencies run: pip install -r dev-requirements.txt - name: Unit tests run: pytest --cov=./readtime --cov-report=xml - name: Upload Coverage uses: codecov/codecov-action@v2 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests alanhamlett-readtime-11868e2/.gitignore000066400000000000000000000005121445667602700200630ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject virtualenv venv .DS_Store alanhamlett-readtime-11868e2/AUTHORS000066400000000000000000000003751445667602700171520ustar00rootroot00000000000000readtime is written and maintained by Alan Hamlett and various contributors: Development Lead ---------------- - Alan Hamlett Patches and Suggestions ----------------------- - Felipe Guedes alanhamlett-readtime-11868e2/CHANGES.md000066400000000000000000000022651445667602700174740ustar00rootroot00000000000000 # CHANGES ## 3.0.0 (2023-07-22) - Drop Python <= 3.7 support. - Add support for Python 3.11. [#8](https://github.com/alanhamlett/readtime/pull/8) - Remove Python2 relics. [#9](https://github.com/alanhamlett/readtime/pull/9) ## 2.0.0 (2022-05-19) - Drop Python 2 support. - Upgrade markdown2 dependency to fix CVE-2018-5773. [#6](https://github.com/alanhamlett/readtime/pull/6) ## 1.1.1 (2019-04-03) - Remove non-ascii character from readme to prevent setuptools error. [#5](https://github.com/alanhamlett/readtime/issues/5) ## 1.1.0 (2019-02-13) - Ability to customize wpm. [#4](https://github.com/alanhamlett/readtime/issues/4) ## 1.0.6 (2018-05-01) - Specify requirement versions as ranges. ## 1.0.5 (2017-03-25) - Support for Python 3.6. ## 1.0.4 (2017-03-25) - Support operations (addition, subtraction, etc) on results. [#2](https://github.com/alanhamlett/readtime/issues/2) ## 1.0.3 (2016-08-24) - Split words on any non-word character, to match Medium formula. ## 1.0.2 (2016-08-24) - Use 265 WPM to match Medium formula. [#1](https://github.com/alanhamlett/readtime/issues/1) ## 1.0.1 (2016-08-23) - Round minutes up. ## 1.0.0 (2016-08-23) - Birth. alanhamlett-readtime-11868e2/LICENSE000066400000000000000000000024771445667602700171140ustar00rootroot00000000000000BSD License =========== Copyright (c) 2016 by Alan Hamlett. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. alanhamlett-readtime-11868e2/MANIFEST.in000066400000000000000000000001271445667602700176330ustar00rootroot00000000000000include README.rst LICENSE CHANGES.md requirements.txt recursive-include readtime *.py alanhamlett-readtime-11868e2/Makefile000066400000000000000000000004541445667602700175400ustar00rootroot00000000000000.PHONY: all test clean build upload all: @echo 'test run the unit tests with the current default python' @echo 'release publish the current version to pypi' test: @pytest release: clean build upload clean: @rm -f dist/* build: @python ./setup.py sdist upload: @twine upload ./dist/* alanhamlett-readtime-11868e2/README.md000066400000000000000000000046351445667602700173640ustar00rootroot00000000000000# readtime [![Tests](https://img.shields.io/github/actions/workflow/status/alanhamlett/readtime/tests.yml?branch=master)](https://github.com/alanhamlett/readtime/actions/workflows/tests.yml) [![Coverage](https://codecov.io/gh/alanhamlett/readtime/branch/master/graph/badge.svg?token=EbUnuwbra3)](https://codecov.io/gh/alanhamlett/readtime) Calculates the time some text takes the average human to read, based on Medium's [read time forumula](https://help.medium.com/hc/en-us/articles/214991667-Read-time). ### Algorithm Medium's Help Center says, > Read time is based on the average reading speed of an adult (roughly 265 WPM). We take the total word count of a post and translate it into minutes, with an adjustment made for images. For posts in Chinese, Japanese and Korean, it's a function of number of characters (500 characters/min) with an adjustment made for images. Source: https://help.medium.com/hc/en-us/articles/214991667-Read-time (Read Sept 23rd, 2018) Double checking with real articles, the English algorithm is: seconds = num_words / 265 * 60 + img_weight * num_images With `img_weight` starting at `12` and decreasing one second with each image encountered, with a minium `img_weight` of `3` seconds. ### Installation virtualenv venv . venv/bin/activate pip install readtime Or if you like to live dangerously: sudo pip install readtime ### Usage Import `readtime` and pass it some text, HTML, or Markdown to get back the time it takes to read: >>> import readtime >>> result = readtime.of_text('The shortest blog post in the world!') >>> result.seconds 2 >>> result.text u'1 min' The result can also be used as a string: >>> str(readtime.of_text('The shortest blog post in the world!')) u'1 min read' To calculate read time of Markdown: >>> readtime.of_markdown('This is **Markdown**') 1 min read To calculate read time of HTML: >>> readtime.of_html('This is HTML') 1 min read To customize the WPM (default 265): >>> result = readtime.of_text('The shortest blog post in the world!', wpm=5) >>> result.seconds 96 >>> result.text u'2 min' >>> result.wpm 5 ### Contributing Before contributing a pull request, make sure tests pass: virtualenv venv . venv/bin/activate pip install tox tox Many thanks to all [contributors](https://github.com/alanhamlett/readtime/blob/master/AUTHORS)! alanhamlett-readtime-11868e2/codecov.yml000066400000000000000000000000531445667602700202400ustar00rootroot00000000000000ignore: - "*/python?.?/*" comment: false alanhamlett-readtime-11868e2/dev-requirements.txt000066400000000000000000000000601445667602700221310ustar00rootroot00000000000000-r requirements.txt coverage pytest pytest-cov alanhamlett-readtime-11868e2/readtime/000077500000000000000000000000001445667602700176675ustar00rootroot00000000000000alanhamlett-readtime-11868e2/readtime/__about__.py000066400000000000000000000006611445667602700221520ustar00rootroot00000000000000__title__ = 'readtime' __description__ = 'Calculates the time some text takes the average human to ' \ 'read, based on Medium\'s read time forumula' __url__ = 'https://github.com/alanhamlett/readtime' __version_info__ = ('3', '0', '0') __version__ = '.'.join(__version_info__) __author__ = 'Alan Hamlett' __author_email__ = 'alan.hamlett@gmail.com' __license__ = 'BSD' __copyright__ = 'Copyright 2016 Alan Hamlett' alanhamlett-readtime-11868e2/readtime/__init__.py000066400000000000000000000004011445667602700217730ustar00rootroot00000000000000""" readtime ~~~~~~~~ Calculates the time some text takes the average human to read. :copyright: (c) 2016 Alan Hamlett. :license: BSD, see LICENSE for more details. """ from .api import ( of_text, of_html, of_markdown, ) alanhamlett-readtime-11868e2/readtime/api.py000066400000000000000000000013441445667602700210140ustar00rootroot00000000000000""" readtime.api ~~~~~~~~~~~~ Contains public methods. :copyright: (c) 2016 Alan Hamlett. :license: BSD, see LICENSE for more details. """ from . import utils def of_text(text, wpm=None): """Get the read time of some text. :param text: String of text (Assumes utf-8). """ return utils.read_time(text, format='text', wpm=wpm) def of_html(html, wpm=None): """Get the read time of some HTML. :param html: String of HTML. """ return utils.read_time(html, format='html', wpm=wpm) def of_markdown(markdown, wpm=None): """Get the read time of some Markdown. :param markdown: String of Markdown. """ return utils.read_time(markdown, format='markdown', wpm=wpm) alanhamlett-readtime-11868e2/readtime/result.py000066400000000000000000000031461445667602700215630ustar00rootroot00000000000000""" readtime.result ~~~~~~~~~~~~~~~ For returning read time results. :copyright: (c) 2016 Alan Hamlett. :license: BSD, see LICENSE for more details. """ import math import operator from datetime import timedelta class Result: delta = None def __init__(self, seconds=None, wpm=None): self.wpm = wpm self.delta = timedelta(seconds=seconds) self._add_operator_methods() def __repr__(self): return self.text + ' read' def __str__(self): return self.__repr__() @property def seconds(self): return int(self.delta.total_seconds()) @property def minutes(self): minutes = math.ceil(self.seconds / 60) minutes = max(1, minutes) # Medium's formula has a minimum of 1 min read time return minutes @property def text(self): return f'{self.minutes} min' def _add_operator_methods(self): for op in dir(operator): can_set = (getattr(self.__class__, op, None) is None and getattr(self.delta, op, None) is not None and op.startswith('__') and op.endswith('__')) if can_set: try: setattr(self.__class__, op, self._create_method(op)) except (AttributeError, TypeError): pass def _create_method(self, op): fn = getattr(self.delta, op) def method(cls, other, *args, **kwargs): delta = fn(other.delta) return Result(seconds=delta.total_seconds(), wpm=self.wpm) return method alanhamlett-readtime-11868e2/readtime/utils.py000066400000000000000000000053231445667602700214040ustar00rootroot00000000000000""" readtime.utils ~~~~~~~~~~~~~~ Utility and non-public methods. :copyright: (c) 2016 Alan Hamlett. :license: BSD, see LICENSE for more details. """ import math import re import lxml import markdown2 from pyquery import PyQuery as pq from .result import Result DEFAULT_WPM = 265 # Medium says they use 275 WPM but they actually use 265 WORD_DELIMITER = re.compile(r'\W+') def read_time(content, format=None, wpm=None): """Returns the read time of some content. :param content: String of content. :param format: Format of the content (html, markdown, or text). """ try: format = format.lower() except: pass if not wpm: wpm = DEFAULT_WPM if format == 'text': seconds = read_time_as_seconds(content, wpm=wpm) elif format == 'markdown': html = markdown2.markdown(content) el = pq(html) text, images = parse_html(el) seconds = read_time_as_seconds(text, images=images, wpm=wpm) elif format == 'html': el = pq(content) text, images = parse_html(el) seconds = read_time_as_seconds(text, images=images, wpm=wpm) else: raise Exception(f'Unsupported format: {format}') return Result(seconds=seconds, wpm=wpm) def read_time_as_seconds(text, images=0, wpm=None): """Returns the read time as seconds of some plain text. :param text: String of plain text. :param images: The number of inline images in the text. """ if not wpm: wpm = DEFAULT_WPM try: num_words = len(re.split(WORD_DELIMITER, text.strip())) except (AttributeError, TypeError): num_words = 0 seconds = math.ceil(num_words / wpm * 60) # add extra seconds for inline images delta = 12 for _ in range(images): seconds += delta if delta > 3: delta -= 1 return seconds def parse_html(el): """Converts HTML to plain text. Returns a tuple of (plain_text, num_images). :param el: A PyQuery DOM object. """ text = [] images = [] paragraphs = ['h1', 'h2', 'h3', 'h4', 'h5'] def add_text(tag, no_tail=False): if tag.tag == 'img': images.append(tag) if tag.text and not isinstance(tag, lxml.etree._Comment): text.append(tag.text) for child in tag.getchildren(): add_text(child) if tag.tag in paragraphs and len(text) > 0 and not text[-1].strip().endswith('.'): text.append('.') if not no_tail and tag.tail: text.append(tag.tail) for tag in el: add_text(tag, no_tail=True) plain_text = re.sub(r'\s+', ' ', ''.join([t for t in text if t])).strip() return plain_text, len(images) alanhamlett-readtime-11868e2/requirements.txt000066400000000000000000000000641445667602700213610ustar00rootroot00000000000000beautifulsoup4>=4.0.1 markdown2>=2.4.3 pyquery>=1.2 alanhamlett-readtime-11868e2/setup.cfg000066400000000000000000000001421445667602700177130ustar00rootroot00000000000000[nosetests] with-coverage = 1 cover-inclusive = 1 cover-package = readtime exclude-dir = venv alanhamlett-readtime-11868e2/setup.py000066400000000000000000000025141445667602700176110ustar00rootroot00000000000000from setuptools import setup about = {} with open('readtime/__about__.py') as f: exec(f.read(), about) packages = [ about['__title__'], ] install_requires = [x.strip() for x in open('requirements.txt').readlines()] setup( name=about['__title__'], version=about['__version__'], license=about['__license__'], description=about['__description__'], long_description=open('README.md').read(), long_description_content_type='text/markdown', author=about['__author__'], author_email=about['__author_email__'], url=about['__url__'], packages=packages, package_dir={about['__title__']: about['__title__']}, include_package_data=True, zip_safe=False, platforms='any', install_requires=install_requires, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Topic :: Software Development :: Build Tools', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', ], ) alanhamlett-readtime-11868e2/tests/000077500000000000000000000000001445667602700172375ustar00rootroot00000000000000alanhamlett-readtime-11868e2/tests/__init__.py000066400000000000000000000000001445667602700213360ustar00rootroot00000000000000alanhamlett-readtime-11868e2/tests/samples/000077500000000000000000000000001445667602700207035ustar00rootroot00000000000000alanhamlett-readtime-11868e2/tests/samples/html.html000066400000000000000000000176161445667602700225500ustar00rootroot00000000000000

Want to add a feature or automate something in your NetBeans IDE? Follow along as we write your first plugin for NetBeans.

Let's go beyond the simple Toolbar Example and create a plugin which can auto-update itself. This code is based on the WakaTime plugin for NetBeans. Our example plugin will simply print a Hello World statement and update to new versions if available... just enough to get you started.

Create a new Plugin Project

Choose File -> New Project then NetBeans Modules -> Module as the project type.

Create Plugin Project

Name your project

Name Your Project

Choose a namespace or code name for your plugin

Namespace Your Project

Add a Java File

Create Java File

Name Java File

Plugin Starting Point

After creating the new Java Class file, make it extend ModuleInstall and wrap it with @OnShowing so it only runs after the GUI has loaded.

java @OnShowing public class MyPlugin extends ModuleInstall implements Runnable { }

Press ALT + ENTER with your cursor over OnShowing then select Search Module Dependency for OnShowing to import the Window System API into the project. This will add a new dependency to your project as well as add the necessary import statements to the top of your file. Also do this for ModuleInstall.

Search Module Dependency

Sometimes NetBeans misses the org.openide.util dependency, so you might have to add that one manually. To do that, right click on MyPlugin then select Properties.

Project Properties

Choose category Libraries then click Add.... Type org.openide.util then click OK. This will add the dependency to your project.xml file.

Project Properties Libraries

Add Utilities API

Press ALT + ENTER on your MyPlugin class, then choose Implement all abstract methods.

Implement Abstract Methods

One last thing, add this line to your manifest.mf file.

OpenIDE-Module-Install: org/myorg/myplugin/MyPlugin.class

OpenIDE Module Install

Now the run() method will execute after your plugin has loaded.

First Time Running

Logging

Let's make that println output to the NetBeans IDE log. First, setup the logger as an attribute of your MyPlugin class.

java public static final Logger log = Logger.getLogger("MyPlugin");

Press ALT + ENTER to import java.util.logging.Logger.

Add Logger Import

Replace println with log.info("MyPlugin has loaded.");.

Log Line

Updating Your Plugin Automatically

Create a new Java file UpdateHandler.java inside your MyPlugin package.

Replace the contents of this file with UpdateHandler.java. Search the module dependency and add any missing dependencies by pressing ALT + ENTER over each import statement.

Add these lines to your manifest.mf file.

java OpenIDE-Module-Layer: org/myorg/myplugin/layer.xml OpenIDE-Module-Implementation-Version: 201501010101

Create a new XML document in your MyPlugin package.

New XML Document

Name XML Document

java <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd"> <filesystem> <folder name="Services"> <folder name="AutoupdateType"> <file name="org_myorg_myplugin_update_center.instance"> <attr name="displayName" bundlevalue="org.myorg.myplugin.Bundle#Services/AutoupdateType/org_myorg_myplugin_update_center.instance"/> <attr name="enabled" boolvalue="true"/> <attr name="instanceCreate" methodvalue="org.netbeans.modules.autoupdate.updateprovider.AutoupdateCatalogFactory.createUpdateProvider"/> <attr name="instanceOf" stringvalue="org.netbeans.spi.autoupdate.UpdateProvider"/> <attr name="url" bundlevalue="org.myorg.myplugin.Bundle#org_myorg_myplugin_update_center"/> </file> </folder> </folder> </filesystem>

Add this code to your MyPlugin class inside the run() method.

java WindowManager.getDefault().invokeWhenUIReady(new Runnable () { @Override public void run() { UpdateHandler.checkAndHandleUpdates(); } });

Add these lines to your Bundle.properties file:

java Services/AutoupdateType/org_myorg_myplugin_update_center.instance=MyPlugin UpdateHandler.NewModules=false org_myorg_myplugin_update_center=https\://example.com/updates.xml

Now every time NetBeans restarts and launches your plugin, it will check for updates by downloading updates.xml from example.com.

Your updates.xml file tells NetBeans where to get the new NBM of your plugin. To create an NBM for publishing your plugin, right click on your MyPlugin project and select Create NBM. The NBM file is what you will publish to the NetBeans Plugin Portal.

For an example of hosting updates.xml on GitHub, look at update.xml and corrosponding Bundle.properties from the WakaTime NetBeans plugin.

alanhamlett-readtime-11868e2/tests/samples/markdown.md000066400000000000000000000153741445667602700230610ustar00rootroot00000000000000Want to add a feature or automate something in your [NetBeans IDE](https://netbeans.org/)? Follow along as we write your first plugin for NetBeans. Let's go beyond the simple [Toolbar Example](https://platform.netbeans.org/tutorials/nbm-google.html) and create a plugin which can auto-update itself. This code is based on the [WakaTime plugin for NetBeans](https://github.com/wakatime/netbeans-wakatime). Our example plugin will simply print a Hello World statement and update to new versions if available... just enough to get you started. ## Create a new Plugin Project Choose `File` -> `New Project` then `NetBeans Modules` -> `Module` as the project type. ![Create Plugin Project](https://wakatime.com/static/img/blog/create-plugin-project.png) Name your project ![Name Your Project](https://wakatime.com/static/img/blog/name-your-project.png) Choose a namespace or code name for your plugin ![Namespace Your Project](https://wakatime.com/static/img/blog/namespace-your-project.png) ## Add a Java File ![Create Java File](https://wakatime.com/static/img/blog/create-java-file.png) ![Name Java File](https://wakatime.com/static/img/blog/name-java-file.png) ## Plugin Starting Point After creating the new Java Class file, make it extend [ModuleInstall](http://bits.netbeans.org/7.4/javadoc/org-openide-modules/org/openide/modules/ModuleInstall.html) and wrap it with [@OnShowing](http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/OnShowing.html) so it only runs after the GUI has loaded. ```java @OnShowing public class MyPlugin extends ModuleInstall implements Runnable { } ``` Press ALT + ENTER with your cursor over `OnShowing` then select `Search Module Dependency for OnShowing` to import the Window System API into the project. This will add a new dependency to your project as well as add the necessary import statements to the top of your file. Also do this for `ModuleInstall`. ![Search Module Dependency](https://wakatime.com/static/img/blog/search-module-dependency.png) Sometimes NetBeans misses the `org.openide.util` dependency, so you might have to add that one manually. To do that, right click on MyPlugin then select `Properties`. ![Project Properties](https://wakatime.com/static/img/blog/project-properties.png) Choose category `Libraries` then click `Add...`. Type `org.openide.util` then click `OK`. This will add the dependency to your `project.xml` file. ![Project Properties Libraries](https://wakatime.com/static/img/blog/project-properties-libraries.png) ![Add Utilities API](https://wakatime.com/static/img/blog/add-utilities-api.png) Press ALT + ENTER on your MyPlugin class, then choose `Implement all abstract methods`. ![Implement Abstract Methods](https://wakatime.com/static/img/blog/implement-abstract-methods.png) One last thing, add this line to your `manifest.mf` file. `OpenIDE-Module-Install: org/myorg/myplugin/MyPlugin.class` ![OpenIDE Module Install](https://wakatime.com/static/img/blog/openide-module-install.png) Now the `run()` method will execute after your plugin has loaded. ![First Time Running](https://wakatime.com/static/img/blog/plugin-has-loaded.png) ## Logging Let's make that `println` output to the NetBeans IDE log. First, setup the logger as an attribute of your MyPlugin class. ```java public static final Logger log = Logger.getLogger("MyPlugin"); ``` Press ALT + ENTER to import [java.util.logging.Logger](https://encrypted.google.com/search?q=java.util.logging.Logger+site%3Ahttps%3A%2F%2Fdocs.oracle.com). ![Add Logger Import](https://wakatime.com/static/img/blog/add-logger-import.png) Replace `println` with `log.info("MyPlugin has loaded.");`. ![Log Line](https://wakatime.com/static/img/blog/log-line.png) ## Updating Your Plugin Automatically Create a new Java file `UpdateHandler.java` inside your MyPlugin package. Replace the contents of this file with [UpdateHandler.java](https://gist.github.com/alanhamlett/2a57ffb51f0850272d0d). Search the module dependency and add any missing dependencies by pressing ALT + ENTER over each import statement. Add these lines to your `manifest.mf` file. ```java OpenIDE-Module-Layer: org/myorg/myplugin/layer.xml OpenIDE-Module-Implementation-Version: 201501010101 ``` Create a new XML document in your MyPlugin package. ![New XML Document](https://wakatime.com/static/img/blog/new-xml-document.png) ![Name XML Document](https://wakatime.com/static/img/blog/name-xml-document.png) ```java ``` Add this code to your MyPlugin class inside the `run()` method. ```java WindowManager.getDefault().invokeWhenUIReady(new Runnable () { @Override public void run() { UpdateHandler.checkAndHandleUpdates(); } }); ``` Add these lines to your `Bundle.properties` file: ```java Services/AutoupdateType/org_myorg_myplugin_update_center.instance=MyPlugin UpdateHandler.NewModules=false org_myorg_myplugin_update_center=https\://example.com/updates.xml ``` Now every time NetBeans restarts and launches your plugin, it will check for updates by downloading `updates.xml` from example.com. Your updates.xml file tells NetBeans where to get the new NBM of your plugin. To create an NBM for publishing your plugin, right click on your MyPlugin project and select `Create NBM`. The NBM file is what you will publish to the [NetBeans Plugin Portal](http://plugins.netbeans.org/). For an example of hosting `updates.xml` on GitHub, look at [update.xml](https://github.com/wakatime/netbeans-wakatime/blob/master/updates.xml) and corrosponding [Bundle.properties](https://github.com/wakatime/netbeans-wakatime/blob/master/src/org/wakatime/netbeans/plugin/Bundle.properties) from the [WakaTime NetBeans plugin](https://github.com/wakatime/netbeans-wakatime/). alanhamlett-readtime-11868e2/tests/samples/plain_text.txt000066400000000000000000000110251445667602700236120ustar00rootroot00000000000000Want to add a feature or automate something in your NetBeans IDE? Follow along as we write your first plugin for NetBeans. Let's go beyond the simple Toolbar Example and create a plugin which can auto-update itself. This code is based on the WakaTime plugin for NetBeans. Our example plugin will simply print a Hello World statement and update to new versions if available... just enough to get you started. Create a new Plugin Project Choose File -> New Project then NetBeans Modules -> Module as the project type. Create Plugin Project Name your project Name Your Project Choose a namespace or code name for your plugin Namespace Your Project Add a Java File Create Java File Name Java File Plugin Starting Point After creating the new Java Class file, make it extend ModuleInstall and wrap it with @OnShowing so it only runs after the GUI has loaded. java @OnShowing public class MyPlugin extends ModuleInstall implements Runnable { } Press ALT + ENTER with your cursor over OnShowing then select Search Module Dependency for OnShowing to import the Window System API into the project. This will add a new dependency to your project as well as add the necessary import statements to the top of your file. Also do this for ModuleInstall. Search Module Dependency Sometimes NetBeans misses the org.openide.util dependency, so you might have to add that one manually. To do that, right click on MyPlugin then select Properties. Project Properties Choose category Libraries then click Add.... Type org.openide.util then click OK. This will add the dependency to your project.xml file. Project Properties Libraries Add Utilities API Press ALT + ENTER on your MyPlugin class, then choose Implement all abstract methods. Implement Abstract Methods One last thing, add this line to your manifest.mf file. OpenIDE-Module-Install: org/myorg/myplugin/MyPlugin.class OpenIDE Module Install Now the run() method will execute after your plugin has loaded. First Time Running Logging Let's make that println output to the NetBeans IDE log. First, setup the logger as an attribute of your MyPlugin class. java public static final Logger log = Logger.getLogger("MyPlugin"); Press ALT + ENTER to import java.util.logging.Logger. Add Logger Import Replace println with log.info("MyPlugin has loaded.");. Log Line Updating Your Plugin Automatically Create a new Java file UpdateHandler.java inside your MyPlugin package. Replace the contents of this file with UpdateHandler.java. Search the module dependency and add any missing dependencies by pressing ALT + ENTER over each import statement. Add these lines to your manifest.mf file. java OpenIDE-Module-Layer: org/myorg/myplugin/layer.xml OpenIDE-Module-Implementation-Version: 201501010101 Create a new XML document in your MyPlugin package. New XML Document Name XML Document java Add this code to your MyPlugin class inside the run() method. java WindowManager.getDefault().invokeWhenUIReady(new Runnable () { @Override public void run() { UpdateHandler.checkAndHandleUpdates(); } }); Add these lines to your Bundle.properties file: java Services/AutoupdateType/org_myorg_myplugin_update_center.instance=MyPlugin UpdateHandler.NewModules=false org_myorg_myplugin_update_center=https\://example.com/updates.xml Now every time NetBeans restarts and launches your plugin, it will check for updates by downloading updates.xml from example.com. Your updates.xml file tells NetBeans where to get the new NBM of your plugin. To create an NBM for publishing your plugin, right click on your MyPlugin project and select Create NBM. The NBM file is what you will publish to the NetBeans Plugin Portal. For an example of hosting updates.xml on GitHub, look at update.xml and corrosponding Bundle.properties from the WakaTime NetBeans plugin. alanhamlett-readtime-11868e2/tests/test_readtime.py000066400000000000000000000074721445667602700224540ustar00rootroot00000000000000import unittest import readtime from readtime.utils import DEFAULT_WPM class BaseTestCase(unittest.TestCase): def test_transitions(self): word = 'word ' for x in range(10): # test the maximum num words for x read time text = word * 265 * x result = readtime.of_text(text) self.assertEqual(result.seconds, x * 60 if x > 0 else 1) self.assertEqual(result.text, f'{x if x > 0 else 1} min') self.assertEqual(str(result), f'{x if x > 0 else 1} min read') # test the maximum + 1 num words, and make sure read time is x + 1 text += 'word' result = readtime.of_text(text) self.assertEqual(result.seconds, x * 60 + 1) self.assertEqual(result.text, f'{x + 1} min') self.assertEqual(str(result), f'{x + 1} min read') def test_plain_text(self): inp = open('tests/samples/plain_text.txt').read() result = readtime.of_text(inp) self.assertEqual(result.seconds, 154) self.assertEqual(type(result.seconds), int) self.assertEqual(result.text, '3 min') self.assertEqual(str(result), '3 min read') def test_plain_text_empty(self): result = readtime.of_text('') self.assertEqual(result.seconds, 1) self.assertEqual(result.text, '1 min') self.assertEqual(str(result), '1 min read') def test_plain_text_null(self): result = readtime.of_text(None) self.assertEqual(result.seconds, 0) self.assertEqual(result.text, '1 min') self.assertEqual(str(result), '1 min read') def test_markdown(self): inp = open('tests/samples/markdown.md').read() result = readtime.of_markdown(inp) self.assertEqual(result.seconds, 236) self.assertEqual(result.text, '4 min') self.assertEqual(str(result), '4 min read') def test_html(self): inp = open('tests/samples/html.html').read() result = readtime.of_html(inp) self.assertEqual(result.seconds, 236) self.assertEqual(result.text, '4 min') self.assertEqual(str(result), '4 min read') def test_plain_text_unicode(self): result = readtime.of_text('Some simple text') self.assertEqual(str(result), '1 min read') def test_unsupported_format(self): with self.assertRaises(Exception) as e: readtime.utils.read_time('Some simple text', format='foo') self.assertEqual(str(e.exception), 'Unsupported format: foo') def test_invalid_format(self): with self.assertRaises(Exception) as e: readtime.utils.read_time('Some simple text', format=123) self.assertEqual(str(e.exception), 'Unsupported format: 123') def test_can_add(self): inp = open('tests/samples/plain_text.txt').read() result1 = readtime.of_text(inp) self.assertEqual(result1.seconds, 154) inp = open('tests/samples/markdown.md').read() result2 = readtime.of_markdown(inp) self.assertEqual(result2.seconds, 236) result = (result1 + result2) self.assertEqual(result.seconds, 154 + 236) self.assertEqual(type(result.seconds), int) self.assertEqual(result.text, '7 min') self.assertEqual(str(result), '7 min read') def test_custom_wpm(self): text = 'some test content ' * 100 result = readtime.of_text(text) self.assertEqual(result.wpm, DEFAULT_WPM) self.assertEqual(result.seconds, 68) self.assertEqual(result.text, '2 min') wpm = 50 result = readtime.of_text(text, wpm=wpm) self.assertEqual(result.wpm, wpm) self.assertEqual(result.seconds, 360) self.assertEqual(type(result.seconds), int) self.assertEqual(result.text, '6 min') self.assertEqual(str(result), '6 min read') alanhamlett-readtime-11868e2/tox.ini000066400000000000000000000001431445667602700174060ustar00rootroot00000000000000[tox] envlist = py38, py39, py310, py311 [testenv] deps = -rdev-requirements.txt commands = pytest