Skip to content

Commit

Permalink
Merge pull request #7 from Paulanerus/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Paulanerus authored Jan 15, 2025
2 parents 9a9f7e2 + eb80999 commit 4254f44
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 88 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# TextExplorer

This is a tool designed for the exploration and comparison of variants of textual data.

## Usage
Expand All @@ -7,8 +8,9 @@ TODO

## Development

TODO
To create plugins for TextExplorer, see the [API ReadMe](api/README.md) for details.

## Support

If you have any problems or questions about this project, please get in touch. You can also [open an issue](https://github.com/Paulanerus/TextExplorer/issues) on GitHub.
If you have any problems or questions about this project, please get in touch. You can
also [open an issue](https://github.com/Paulanerus/TextExplorer/issues) on GitHub.
279 changes: 279 additions & 0 deletions api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
# API Development

This ReadMe provides an overview of the TextExplorer API and guides you through developing your own plugin. A sample
implementation is available in [plugins/demo](../plugins/demo).

While using a build tool is not mandatory, this documentation employs [Gradle](https://gradle.org/) in its examples to
align with the project's overall setup.

## Environment setup

1. Install Java 21.
2. Create a new Gradle (Kotlin DSL) project.
3. Download the latest [API release](https://github.com/Paulanerus/TextExplorer/releases/latest) and add the JAR as a
local dependency:

```kotlin
implementation(files("libs/api-0.32.2.jar"))
```

4. Reload the Gradle project.

## First plugin

To create your first plugin, follow these steps:

1. Create a Kotlin class, e.g., `PluginMain`, and implement the `IPlugin` interface:

```kotlin
class PluginMain : IPlugin {
override fun init() {
println("Hello world from plugin.")
}
}
```

The `init` function is executed when the application starts or the plugin is loaded via the interface. Use this function
to initialize external services or perform setup tasks.

2. Annotate the class with `@PluginMetadata` to declare the plugin's metadata. You can include additional information
like the version, author, or a short description.

3. Optionally, use the `@PluginOrder` annotation to specify the load order for the plugin.

4. Update the Gradle `jar` task to include the plugin's main class in the JAR file manifest:

```kotlin
tasks.jar {
manifest {
attributes["Main-Class"] = "PluginMain" // Adjust the path if necessary
}
}
```

## Provide data

To connect existing data to your plugin, annotate it with `@RequiresData` and specify the identifier of the data pool.
For example, to connect to `demo_data`, annotate the class with:
`@RequiresData("demo_data")`.

To import your own data, a "data-layout" is required. It's recommended to create a separate Kotlin file (e.g.,
`Data.kt`) containing the relevant classes for the data structure.

Consider the following CSV files:

**authors.csv:**

```text
author_id,full_name,year_born,birth_place
...,...,...,...
...
```

**quotes.csv:**

```text
quote,date,author_id
...,...,...,...
...
```

In Data.kt, define your data classes to match the column names in the CSV files:

```kotlin
@DataSource("authors")
data class Author(
@Unique(true) val author_id: Long,
val full_name: String,
val year_born: String
)

@DataSource("quotes")
data class Quote(
@Index(Language.ENGLISH) val quote: String,
val date: String,
@Link(Author::class) val author_id: Long
)
```

+ `@DataSource`: Links the respective CSV file to the class. Ensure the class name matches the CSV name (the file
extension is optional).

+ `@Unique`: Marks the field as unique. If `true` and the type is `Long`, it will serve as the identifier and will not
be auto-generated by TextExplorer.

+ `@Index`: Marks the field as indexed in the specified language, which is essential for search functionality. It can be
marked as the default value and must occur at least once in any class.

+ `@Link`: Establishes a relationship between two classes. Use this with `@Index` to enable related data display in the
interface. The field name must be the same in both classes.

Finally, update the plugin to include the created data classes as part of its data requirements:

```kotlin
@RequiresData("demo_data", sources = [Author::class, Quote::class])
class PluginMain : IPlugin {
...
}
```

This will import the data appropriately, making it available for use in TextExplorer.

**Currently only data in csv-files is supported.**

## Variants

Different terms can refer to the same entity, such as "USA," "US," "United States," or "America" for the country.
TextExplorer provides the `Variant API` to handle these cases.

With the Variant API, you can define a column in a CSV file as the "base" term (the one the user will search for), and
additional columns can store variant terms. This allows users to search for any variant.

**Example: Countries**

**countries.csv**

```text
base,variant
USA,USA
USA,US
USA,United States
USA,America
...
```

In this example, "USA" is the base term, and the other terms are variants. The TextExplorer representation would look
like this:

```kotlin
@Variant(base = "base", ["variant"])
@DataSource("countries")
data class Country(val base: String, val variant: String)
```

To connect this data to your plugin, you need to update the `@RequiresData` annotation to include the `Country` class:

```kotlin
@RequiresData("countries", sources = [Country::class, Author::class, Quote::class])
class PluginMain : IPlugin {
...
}
```

This setup allows the user to search for `@countries:USA`, and TextExplorer will return all entries that match any of
the variants declared for "USA."

## Tagging API

The Tagging API allows you to highlight specific words (e.g., names) within the Tagging View.

To implement this functionality, simply implement the tag function from the Taggable interface in your Plugin Main class.
This function takes the field name and its corresponding value as parameters.

Additionally, annotate the function with `@ViewFilter`, which specifies a filter name and the fields it accepts.
Optionally, you can use the global parameter to apply the tags to the DiffView.

To highlight the name "Tom" in every field, the implementation would look like this:

```kotlin
@ViewFilter("Name Highlighter", fields = ["quote"], global = true)
override fun tag(field: String, value: String): Map<String, Tag> = mapOf("Tom" to Tag("NAME", Color.blue))
```

In this example:

+ Only the quote field is passed to the tag function.
+ The word "Tom" is mapped to the Tag with the identifier `NAME` and the color blue.
+ Every occurrence of "Tom" in the quote field will be highlighted in blue.

## Pre filtering

**Not yet implemented**

## Access plugin data

**Not yet implemented**

## Export plugin

To export the plugin, run the Gradle `jar` task:

```shell
./gradlew jar
```

For Windows use:

```shell
gradlew.bat jar
```

### A full example:

**PluginMain:**

```kotlin
@PluginOrder(3)
@RequiresData("demo_data", sources = [Country::class, Author::class, Quote::class])
@PluginMetadata("demo", author = "Author", version = "1.0.0", description = "A short description.")
class PluginMain : IPlugin, Taggable {

override fun init() {
println("Hello world from plugin.")
}

@ViewFilter("Name Highlighter", fields = ["quote"], global = true)
override fun tag(field: String, value: String): Map<String, Tag> = mapOf("Tom" to Tag("NAME", Color.blue))
}
```

**Data.kt:**

```kotlin
@Variant(base = "base", ["variant"])
@DataSource("countries")
data class Country(val base: String, val variant: String)

@DataSource("authors")
data class Author(
@Unique(true) val author_id: Long,
val full_name: String,
val year_born: String
)

@DataSource("quotes")
data class Quote(
@Index(Language.ENGLISH) val quote: String,
val date: String,
@Link(Author::class) val author_id: Long
)
```

**build.gradle.kts**

```kotlin
plugins {
kotlin("jvm") version "2.0.21"
}

group = "com.example"
version = "1.0-SNAPSHOT"

repositories {
mavenCentral()
}

dependencies {
implementation(files("libs/api-0.32.2.jar"))
}

tasks.jar {
manifest {
attributes["Main-Class"] = "PluginMain"
}
}

kotlin {
jvmToolchain(21)
}
```
3 changes: 3 additions & 0 deletions api/src/main/kotlin/dev/paulee/api/data/Data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ enum class Language {
@Target(AnnotationTarget.CLASS)
annotation class Variant(val base: String, val variants: Array<String>)

@Target(AnnotationTarget.CLASS)
annotation class PreFilter(val key: String, val linkKey: String, val value: String)

@Target(AnnotationTarget.CLASS)
annotation class RequiresData(val name: String, val sources: Array<KClass<*>> = [])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ interface IStorageProvider : Closeable {
name: String,
ids: Set<Long> = emptySet<Long>(),
whereClause: List<String> = emptyList<String>(),
filter: List<String> = emptyList(),
offset: Int = 0,
limit: Int = Int.MAX_VALUE
limit: Int = Int.MAX_VALUE,
): List<Map<String, String>>

fun count(
name: String,
ids: Set<Long> = emptySet<Long>(),
whereClause: List<String> = emptyList<String>()
whereClause: List<String> = emptyList<String>(),
filter: List<String> = emptyList(),
): Long
}
Loading

0 comments on commit 4254f44

Please sign in to comment.