From eea8b8eb1511dde87f8a2d134817e3592455959d Mon Sep 17 00:00:00 2001 From: Karel Douda Date: Sat, 18 Jan 2025 17:09:56 +0100 Subject: [PATCH] Update documentation --- src/codegen/generate_docs.py | 69 +++++++++++++++ src/pyrdfrules/__init__.py | 101 ++++++++++++++++----- src/pyrdfrules/rdfrules/__init__.py | 130 ++++++++++++++++++++++++++++ src/pyrdfrules/rdfrules/pipeline.py | 45 ++++++++-- src/pyrdfrules/rdfrules/release.py | 3 + 5 files changed, 322 insertions(+), 26 deletions(-) create mode 100644 src/codegen/generate_docs.py diff --git a/src/codegen/generate_docs.py b/src/codegen/generate_docs.py new file mode 100644 index 0000000..5d4e1bb --- /dev/null +++ b/src/codegen/generate_docs.py @@ -0,0 +1,69 @@ +import os +from typing import List + + + +def format_markdown_table(data: List[dict]): + from markdown_table_generator import generate_markdown, table_from_string_list + table = table_from_string_list(data) + return generate_markdown(table) + + +def format_rdfrules_module_docstring(): + + pipeline_file = "src/pyrdfrules/rdfrules/pipeline.py" + docfile = "src/pyrdfrules/rdfrules/__init__.py" + + def get_pipeline_tasks(): + class_names = [["Operation", "Class"]] + + with open(pipeline_file, "r") as f: + lines = f.readlines() + for line in lines: + if "class" in line and "RDFRulesTaskModel" in line: + class_name = line.split(" ")[1].split("(")[0] + + if class_name == "RDFRulesTaskModel": # skip the base class + continue + + if class_name == "ArbitraryPipelineTask": + class_names.append(["ArbitraryPipelineTask", f"pyrdfrules.rdfrules.pipeline.{class_name}()"]) + continue + + # camel case to slug + slug_name = ''.join([f"-{i.lower()}" if i.isupper() else i for i in class_name]).lstrip("-") + + link = "https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#{0}".format(slug_name) + + markdown_link = f"[{class_name}]({link})" + + class_names.append([markdown_link, f"pyrdfrules.rdfrules.pipeline.{class_name}()"]) + + + return class_names + + docstring_rdfrules = "" + + with open(docfile, "r") as f: + lines = f.read() + print(lines) + + start = lines.find("") + 37 + end = lines.find("") + + print(start, end) + + first_part = lines[:start] + second_part = lines[end:] + + lines = first_part + "\n" + format_markdown_table(get_pipeline_tasks()) + "\n" + second_part + + docstring_rdfrules = lines + + print(docstring_rdfrules) + + with open(docfile, "w") as f: + f.write(docstring_rdfrules) + + +format_rdfrules_module_docstring() \ No newline at end of file diff --git a/src/pyrdfrules/__init__.py b/src/pyrdfrules/__init__.py index ff64d2f..883ca6a 100644 --- a/src/pyrdfrules/__init__.py +++ b/src/pyrdfrules/__init__.py @@ -5,6 +5,65 @@ """ PyRDFRules is a Python wrapper for the RDFRules tool, providing an interface to interact with RDFRules for rule mining from RDF knowledge graphs. +## Features + +- Start and stop the RDFRules engine. +- Provision a local instance of RDFRules. +- Create and run tasks. +- Access the results of the tasks. +- Format the results of the tasks. + +## Quickstart + +If you want to get started with PyRDFRules instantly, you can use one of the two following Google Colab notebooks: + +* [Template RDFRules Notebook](https://colab.research.google.com/drive/1KCyv7b6RtQgQXk-V-oTjYpiQsC-_mFHp?usp=sharing) - use this notebook as a start for your analysis workloads, provisions the PyRDFRules library and local RDFRules. +* [Pipeline sample](https://colab.research.google.com/drive/192YaNsbpqoD9-he32OaY2nTi-E_ctXYT?usp=sharing) - a sample pipeline on a local instance of RDFRules, from starting the instance to getting the results. + +## Installation + +1. Install the package using pip: +```bash +pip install pyrdfrules +``` + +2. Configure the RDFRules instance ahead of time using the `Config` class: + +```python +from pyrdfrules.config import Config + +config = Config() +``` + +3. Start a local instance of RDFRules: +```python +app = pyrdfrules.application.Application() + +rdfrules = app.start_local( + install_jvm = True, + install_rdfrules = True, + config = config +) +``` + +## Modules + +The library is segmented into the following modules: + +* `pyrdfrules.api` - internal API classes. +* `pyrdfrules.application` - provides methods to start and stop local or remote instances of RDFRules. +* `pyrdfrules.common` - contains common classes and methods. +* `pyrdfrules.config` - configuration class. +* `pyrdfrules.engine` - contains the engine classes, responsible for the lifetime of the RDFRules instance. +* `pyrdfrules.rdfrules` - contains wrappers around RDFRules objects. + +## Supported operations + +Supported operations and bindings of serialized items for each domain can be found at: +* `pyrdfrules.rdfrules` - pipeline operations, + +## Sample pipeline + Sample usage: ```python import pyrdfrules.application @@ -27,54 +86,54 @@ # Create a pipeline, a sequence of steps to be executed. # You do not have to use fully qualified names for the classes, as they are imported in the example. -pipeline = pyrdfrules.rdfrules.pipeline.Pipeline( +pipeline = Pipeline( tasks=[ - pyrdfrules.rdfrules.pipeline.LoadGraph( + LoadGraph( graphName = "", path = "/dbpedia_yago/mappingbased_objects_sample.ttl" ), - pyrdfrules.rdfrules.pipeline.LoadGraph( + LoadGraph( graphName = "", path = "/dbpedia_yago/yagoFacts.tsv", settings = "tsvParsedUris" ), - pyrdfrules.rdfrules.pipeline.LoadGraph( + LoadGraph( graphName = "", path = "/dbpedia_yago/yagoDBpediaInstances.tsv", settings = "tsvParsedUris" ), - pyrdfrules.rdfrules.pipeline.MergeDatasets(), - pyrdfrules.rdfrules.jsonformats.AddPrefixes( + MergeDatasets(), + AddPrefixes( prefixes=[ - pyrdfrules.rdfrules.jsonformats.PrefixFull(prefix="dbo", nameSpace="http://dbpedia.org/ontology/"), - pyrdfrules.rdfrules.jsonformats.PrefixFull(prefix="dbr", nameSpace="http://dbpedia.org/resource/") + PrefixFull(prefix="dbo", nameSpace="http://dbpedia.org/ontology/"), + PrefixFull(prefix="dbr", nameSpace="http://dbpedia.org/resource/") ] ), - pyrdfrules.rdfrules.pipeline.Index(train=[], test=[]), - pyrdfrules.rdfrules.pipeline.Mine( + Index(train=[], test=[]), + Mine( thresholds=[ - pyrdfrules.rdfrules.commondata.Threshold(name="MinHeadSize", value=100), - pyrdfrules.rdfrules.commondata.Threshold(name="MaxRuleLength", value=3), - pyrdfrules.rdfrules.commondata.Threshold(name="Timeout", value=5), - pyrdfrules.rdfrules.commondata.Threshold(name="MinHeadCoverage", value=0.01), + Threshold(name="MinHeadSize", value=100), + Threshold(name="MaxRuleLength", value=3), + Threshold(name="Timeout", value=5), + Threshold(name="MinHeadCoverage", value=0.01), ], ruleConsumers=[ - pyrdfrules.rdfrules.commondata.RuleConsumer( - name=pyrdfrules.rdfrules.commondata.RuleConsumerType.TOP_K, + RuleConsumer( + name=RuleConsumerType.TOP_K, k=1000, allowOverflow=False ) ], patterns=[], constraints=[ - pyrdfrules.rdfrules.commondata.Constraint(name="WithoutConstants") + Constraint(name="WithoutConstants") ], parallelism=0 ), - pyrdfrules.rdfrules.pipeline.ComputeConfidence(confidenceType=ConfidenceType.PCA_CONFIDENCE, min=0.5, topk=50), - pyrdfrules.rdfrules.pipeline.SortRuleset(by=[]), - pyrdfrules.rdfrules.pipeline.GraphAwareRules(), - pyrdfrules.rdfrules.pipeline.GetRules() + ComputeConfidence(confidenceType=ConfidenceType.PCA_CONFIDENCE, min=0.5, topk=50), + SortRuleset(by=[]), + GraphAwareRules(), + GetRules() ] ) diff --git a/src/pyrdfrules/rdfrules/__init__.py b/src/pyrdfrules/rdfrules/__init__.py index b6d201f..f227914 100644 --- a/src/pyrdfrules/rdfrules/__init__.py +++ b/src/pyrdfrules/rdfrules/__init__.py @@ -1,2 +1,132 @@ """Represents internal RDFRules representations of entities, and constants. + +We provide a set of classes that represent RDFRules entities, such as rules, constraints, and thresholds. These classes are used to interact with the RDFRules engine, and are serialized or deserialized from JSON. + +## Pipeline operations + +### Pipeline + +`pyrdfrules.rdfrules.pipeline.Pipeline` is a class that represents a sequence of tasks that can be executed by the RDFRules engine. + +Items to the pipeline are added as a list of tasks, represents the exact order in which they will be executed. + +### Pipeline tasks + +A pipeline task is a unit of work that can be executed by the RDFRules engine. +Parameters are passed to the task to configure its behavior. For example, a `LoadGraph` task requires a `graphName` and a `path` parameter. + +```python +from pyrdfrules.rdfrules.pipeline import LoadGraph + +load_task = LoadGraph( + graphName = "", + path = "/dbpedia_yago/mappingbased_objects_sample.ttl" +) +``` + +Arbitrary parameters can be passed to the task as a dictionary. + +```python +from pyrdfrules.rdfrules.pipeline import LoadGraph + +load_task = LoadGraph( + graphName = "", + path = "/dbpedia_yago/mappingbased_objects_sample.ttl", + extra = { + "key": "value" + } +) +``` + +Be aware that this extra behaviour is not very maintainable, as the parameters are not checked for correctness. + +### ArbitraryPipelineTask + +The ArbitraryPipelineTask operation is a placeholder for any task that is not yet implemented in the library, or the parameters of the task are not represented yet. + +Sample usage: +```python +from pyrdfrules.rdfrules.pipeline import ArbitraryPipelineTask + +task = ArbitraryPipelineTask( + name = "SomeName", + parameters = { + "key": "value" + } +) +``` + +### List of operations + + +| Operation | Class | +|--------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------| +| ArbitraryPipelineTask | pyrdfrules.rdfrules.pipeline.ArbitraryPipelineTask() | +| [LoadGraph](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-graph) | pyrdfrules.rdfrules.pipeline.LoadGraph() | +| [LoadDataset](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-dataset) | pyrdfrules.rdfrules.pipeline.LoadDataset() | +| [LoadRulesetWithoutIndex](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-ruleset-without-index) | pyrdfrules.rdfrules.pipeline.LoadRulesetWithoutIndex() | +| [LoadPredictionWithoutIndex](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-prediction-without-index) | pyrdfrules.rdfrules.pipeline.LoadPredictionWithoutIndex() | +| [MergeDatasets](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#merge-datasets) | pyrdfrules.rdfrules.pipeline.MergeDatasets() | +| [MapQuads](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#map-quads) | pyrdfrules.rdfrules.pipeline.MapQuads() | +| [FilterQuads](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#filter-quads) | pyrdfrules.rdfrules.pipeline.FilterQuads() | +| [Shrink](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#shrink) | pyrdfrules.rdfrules.pipeline.Shrink() | +| [Split](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#split) | pyrdfrules.rdfrules.pipeline.Split() | +| [AddPrefixes](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#add-prefixes) | pyrdfrules.rdfrules.pipeline.AddPrefixes() | +| [Prefixes](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#prefixes) | pyrdfrules.rdfrules.pipeline.Prefixes() | +| [Discretize](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#discretize) | pyrdfrules.rdfrules.pipeline.Discretize() | +| [DiscretizeInBulk](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#discretize-in-bulk) | pyrdfrules.rdfrules.pipeline.DiscretizeInBulk() | +| [Cache](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#cache) | pyrdfrules.rdfrules.pipeline.Cache() | +| [Index](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#index) | pyrdfrules.rdfrules.pipeline.Index() | +| [ExportQuads](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#export-quads) | pyrdfrules.rdfrules.pipeline.ExportQuads() | +| [ExportIndex](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#export-index) | pyrdfrules.rdfrules.pipeline.ExportIndex() | +| [ExportPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#export-prediction) | pyrdfrules.rdfrules.pipeline.ExportPrediction() | +| [GetQuads](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#get-quads) | pyrdfrules.rdfrules.pipeline.GetQuads() | +| [Size](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#size) | pyrdfrules.rdfrules.pipeline.Size() | +| [Properties](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#properties) | pyrdfrules.rdfrules.pipeline.Properties() | +| [Histogram](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#histogram) | pyrdfrules.rdfrules.pipeline.Histogram() | +| [LoadIndex](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-index) | pyrdfrules.rdfrules.pipeline.LoadIndex() | +| [CacheIndex](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#cache-index) | pyrdfrules.rdfrules.pipeline.CacheIndex() | +| [ToDataset](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#to-dataset) | pyrdfrules.rdfrules.pipeline.ToDataset() | +| [ToDatasetWithIntervals](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#to-dataset-with-intervals) | pyrdfrules.rdfrules.pipeline.ToDatasetWithIntervals() | +| [ToDatasetPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#to-dataset-prediction) | pyrdfrules.rdfrules.pipeline.ToDatasetPrediction() | +| [ToDatasetPredictionTasks](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#to-dataset-prediction-tasks) | pyrdfrules.rdfrules.pipeline.ToDatasetPredictionTasks() | +| [ToPredictions](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#to-predictions) | pyrdfrules.rdfrules.pipeline.ToPredictions() | +| [ToPredictionTasks](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#to-prediction-tasks) | pyrdfrules.rdfrules.pipeline.ToPredictionTasks() | +| [DiscretizeTask](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#discretize-task) | pyrdfrules.rdfrules.pipeline.DiscretizeTask() | +| [Mine](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#mine) | pyrdfrules.rdfrules.pipeline.Mine() | +| [LoadRuleset](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-ruleset) | pyrdfrules.rdfrules.pipeline.LoadRuleset() | +| [LoadPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#load-prediction) | pyrdfrules.rdfrules.pipeline.LoadPrediction() | +| [Predict](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#predict) | pyrdfrules.rdfrules.pipeline.Predict() | +| [Prune](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#prune) | pyrdfrules.rdfrules.pipeline.Prune() | +| [FilterRules](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#filter-rules) | pyrdfrules.rdfrules.pipeline.FilterRules() | +| [FilterPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#filter-prediction) | pyrdfrules.rdfrules.pipeline.FilterPrediction() | +| [FilterPredictionTasks](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#filter-prediction-tasks) | pyrdfrules.rdfrules.pipeline.FilterPredictionTasks() | +| [SelectPredictions](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#select-predictions) | pyrdfrules.rdfrules.pipeline.SelectPredictions() | +| [WithModes](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#with-modes) | pyrdfrules.rdfrules.pipeline.WithModes() | +| [GroupPredictions](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#group-predictions) | pyrdfrules.rdfrules.pipeline.GroupPredictions() | +| [Evaluate](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#evaluate) | pyrdfrules.rdfrules.pipeline.Evaluate() | +| [ShrinkRuleset](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#shrink-ruleset) | pyrdfrules.rdfrules.pipeline.ShrinkRuleset() | +| [ShrinkPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#shrink-prediction) | pyrdfrules.rdfrules.pipeline.ShrinkPrediction() | +| [ShrinkPredictionTasks](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#shrink-prediction-tasks) | pyrdfrules.rdfrules.pipeline.ShrinkPredictionTasks() | +| [SortRuleset](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#sort-ruleset) | pyrdfrules.rdfrules.pipeline.SortRuleset() | +| [SortPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#sort-prediction) | pyrdfrules.rdfrules.pipeline.SortPrediction() | +| [ComputeConfidence](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#compute-confidence) | pyrdfrules.rdfrules.pipeline.ComputeConfidence() | +| [ComputeSupport](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#compute-support) | pyrdfrules.rdfrules.pipeline.ComputeSupport() | +| [MakeClusters](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#make-clusters) | pyrdfrules.rdfrules.pipeline.MakeClusters() | +| [FindSimilar](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#find-similar) | pyrdfrules.rdfrules.pipeline.FindSimilar() | +| [CacheRuleset](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#cache-ruleset) | pyrdfrules.rdfrules.pipeline.CacheRuleset() | +| [CachePrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#cache-prediction) | pyrdfrules.rdfrules.pipeline.CachePrediction() | +| [CachePredictionTasks](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#cache-prediction-tasks) | pyrdfrules.rdfrules.pipeline.CachePredictionTasks() | +| [Instantiate](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#instantiate) | pyrdfrules.rdfrules.pipeline.Instantiate() | +| [GraphAwareRules](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#graph-aware-rules) | pyrdfrules.rdfrules.pipeline.GraphAwareRules() | +| [ExportRules](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#export-rules) | pyrdfrules.rdfrules.pipeline.ExportRules() | +| [PropertiesCardinalities](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#properties-cardinalities) | pyrdfrules.rdfrules.pipeline.PropertiesCardinalities() | +| [GetRules](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#get-rules) | pyrdfrules.rdfrules.pipeline.GetRules() | +| [GetPrediction](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#get-prediction) | pyrdfrules.rdfrules.pipeline.GetPrediction() | +| [GetPredictionTasks](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#get-prediction-tasks) | pyrdfrules.rdfrules.pipeline.GetPredictionTasks() | +| [RulesetSize](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#ruleset-size) | pyrdfrules.rdfrules.pipeline.RulesetSize() | +| [PredictionSize](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#prediction-size) | pyrdfrules.rdfrules.pipeline.PredictionSize() | +| [PredictionTasksSize](https://github.com/propi/rdfrules/blob/master/gui/webapp/README.md#prediction-tasks-size) | pyrdfrules.rdfrules.pipeline.PredictionTasksSize() | + + """ \ No newline at end of file diff --git a/src/pyrdfrules/rdfrules/pipeline.py b/src/pyrdfrules/rdfrules/pipeline.py index f459b29..64fb4e8 100644 --- a/src/pyrdfrules/rdfrules/pipeline.py +++ b/src/pyrdfrules/rdfrules/pipeline.py @@ -1,13 +1,12 @@ from __future__ import annotations -from typing import List, Literal, Optional, Union +from typing import List, Literal, Never, Optional, Union from pydantic import AnyUrl, BaseModel, model_serializer from enum import Enum from pyrdfrules.rdfrules.commondata import ConfidenceType, Constraint, RuleConsumer, SupportType, Threshold from pyrdfrules.rdfrules.jsonformats import PrefixFull - class Pipeline(BaseModel): """Pipeline is a sequence of tasks to be executed. """ @@ -20,19 +19,55 @@ def serialize_model(self): pass class RDFRulesTaskModel(BaseModel): - + """Base class for RDFRules tasks. + + Attributes: + name (Literal): The name of the task. + """ + + extra: Optional[dict] = None + """ + Extra parameters of the task. + """ + @model_serializer() def serialize_model(self): + """@private""" parameters = {} for key, value in vars(self).items(): - if key != 'name' and value is not None: + if key != 'name' and key != 'extra' and value is not None: parameters[key] = value - return {'name': self.name, 'parameters': parameters} + result = {'name': self.name, 'parameters': parameters} + + if self.extraParameters is not None and len(self.extraParameters) > 0: + result.update(self.extraParameters) + + return result pass +class ArbitraryPipelineTask(RDFRulesTaskModel): + name: str + """Any task that is not supported by the library can be represented by this class. + """ + + parameters: dict + """Raw parameters of the task. + """ + + extra: Never + """Extra parameters are not take into account. + """ + + @model_serializer() + def serialize_model(self): + """@private""" + + return {'name': self.name, 'parameters': self.parameters} + + pass class LoadGraph(RDFRulesTaskModel): name: Literal["LoadGraph"] = "LoadGraph" diff --git a/src/pyrdfrules/rdfrules/release.py b/src/pyrdfrules/rdfrules/release.py index 45e6379..2ecbc45 100644 --- a/src/pyrdfrules/rdfrules/release.py +++ b/src/pyrdfrules/rdfrules/release.py @@ -1,3 +1,6 @@ +""" +Release information for RDFRules. This file is used to store information about the current release of RDFRules, where to download it, and its version. +""" RDFRULES_DOWNLOAD_URI = "https://github.com/propi/rdfrules/releases/download/1.9.0/rdfrules-1.9.0.zip"