Browse Source

implemented tag cloud

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
module/tags
Stephan Richter 3 weeks ago
parent
commit
bda42eb15a
  1. 2
      frontend/src/App.svelte
  2. 1
      frontend/src/Components/Menu.svelte
  3. 40
      frontend/src/routes/tags/Index.svelte
  4. 3
      markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java
  5. 1
      tags/src/main/java/de/srsoftware/umbrella/tags/Constants.java
  6. 19
      tags/src/main/java/de/srsoftware/umbrella/tags/SqliteDb.java
  7. 3
      tags/src/main/java/de/srsoftware/umbrella/tags/TagDB.java
  8. 18
      tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java
  9. 1
      translations/src/main/resources/de.json
  10. 9
      web/src/main/resources/web/css/default.css

2
frontend/src/App.svelte

@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
import ResetPw from "./routes/user/ResetPw.svelte";
import Search from "./routes/search/Search.svelte";
import SendDoc from "./routes/document/Send.svelte";
import TagList from "./routes/tags/Index.svelte";
import TagUses from "./routes/tags/TagUses.svelte";
import TaskList from "./routes/task/Index.svelte";
import Times from "./routes/time/Index.svelte";
@ -96,6 +97,7 @@ @@ -96,6 +97,7 @@
<Route path="/project/:id/kanban" component={Kanban} />
<Route path="/project/:id/view" component={ViewPrj} />
<Route path="/search" component={Search} />
<Route path="/tags" component={TagList} />
<Route path="/tags/use/:tag" component={TagUses} />
<Route path="/task" component={TaskList} />
<Route path="/task/:parent_task_id/add_subtask" component={AddTask} />

1
frontend/src/Components/Menu.svelte

@ -56,6 +56,7 @@ onMount(fetchModules); @@ -56,6 +56,7 @@ onMount(fetchModules);
<a href="/company" {onclick}>{t('companies')}</a>
<a href="/project" {onclick}>{t('projects')}</a>
<a href="/task" {onclick}>{t('tasks')}</a>
<a href="/tags" {onclick}>{t('tags')}</a>
<a href="/document" {onclick}>{t('documents')}</a>
<a href="/bookmark" {onclick}>{t('bookmarks')}</a>
<a href="/notes" {onclick}>{t('notes')}</a>

40
frontend/src/routes/tags/Index.svelte

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
<script>
import { useTinyRouter } from 'svelte-tiny-router';
import { onMount } from 'svelte';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let router = useTinyRouter();
let tags = $state(null);
function onclick(e){
e.preventDefault();
var target = e.target;
while (target && !target.href) target=target.parentNode;
let href = target.getAttribute('href');
if (href) router.navigate(href);
return false;
}
async function loadTags(){
const url = api('tags');
const res = await fetch(url,{credentials:'include'});
if (res.ok){
yikes();
tags = await res.json();
} else error(res);
}
onMount(loadTags)
</script>
<h1>{t('tags')}</h1>
<div class="cloud">
{#if tags}
{#each tags as entry}
<a href="/tags/use/{encodeURIComponent(entry.tag)}" class="tag" style="font-size: {40 - 28/entry.count}px" title={t('count_of_occurrences',entry)} {onclick} >{entry.tag}</a>
{/each}
{/if}
</div>

3
markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java

@ -6,15 +6,12 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.userService; @@ -6,15 +6,12 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.BaseHandler;
import de.srsoftware.umbrella.core.ModuleRegistry;
import de.srsoftware.umbrella.core.Util;
import de.srsoftware.umbrella.core.api.MarkdownService;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Token;
import java.io.IOException;
import java.util.Optional;
public class MarkdownApi extends BaseHandler implements MarkdownService {

1
tags/src/main/java/de/srsoftware/umbrella/tags/Constants.java

@ -6,6 +6,7 @@ public class Constants { @@ -6,6 +6,7 @@ public class Constants {
public static final String COMMENT_HASH = "comment_hash";
public static final String CONFIG_DATABASE = "umbrella.modules.tags.database";
public static final String COUNT = "count";
public static final String TABLE_COMMENTS = "comments";
public static final String TABLE_TAGS = "tags";
public static final String TABLE_TAGS_NEW = "tags_new";

19
tags/src/main/java/de/srsoftware/umbrella/tags/SqliteDb.java

@ -17,6 +17,7 @@ import static java.lang.System.Logger.Level.*; @@ -17,6 +17,7 @@ import static java.lang.System.Logger.Level.*;
import static java.text.MessageFormat.format;
import static java.time.ZoneOffset.UTC;
import de.srsoftware.tools.Tuple;
import de.srsoftware.tools.jdbc.Query;
import de.srsoftware.umbrella.bookmarks.BookmarkDb;
import de.srsoftware.umbrella.core.BaseDb;
@ -269,6 +270,24 @@ CREATE TABLE IF NOT EXISTS {0} ( @@ -269,6 +270,24 @@ CREATE TABLE IF NOT EXISTS {0} (
}
}
@Override
public Collection<Tuple<String,Long>> list(long userId) {
try {
var rs = select("tag, COUNT(tag) as count").from(TABLE_TAGS).where(USER_ID,equal(userId)).groupBy(TAG).sort(TAG).exec(db);
var list = new ArrayList<Tuple<String,Long>>();
while (rs.next()) {
var tag = rs.getString(TAG);
if (tag.isBlank()) continue;;
var count = rs.getLong("count");
list.add(Tuple.of(tag,count));
}
rs.close();
return list;
} catch (SQLException e) {
throw databaseException("Failed to load tags for user {0}",userId);
}
}
@Override
public Map<Long, ? extends Collection<String>> list(long userId, String module, Collection<Long> entityIds) {
try {

3
tags/src/main/java/de/srsoftware/umbrella/tags/TagDB.java

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
package de.srsoftware.umbrella.tags;
import de.srsoftware.tools.Tuple;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -14,6 +15,8 @@ public interface TagDB { @@ -14,6 +15,8 @@ public interface TagDB {
Map<String, List<Long>> getUses(String tag, long id);
Collection<Tuple<String, Long>> list(long userId);
Set<String> list(long userId, String module, long entityId);
/**

18
tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java

@ -7,8 +7,7 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.userService; @@ -7,8 +7,7 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.unprocessable;
import static de.srsoftware.umbrella.tags.Constants.CONFIG_DATABASE;
import static de.srsoftware.umbrella.tags.Constants.TAG;
import static de.srsoftware.umbrella.tags.Constants.*;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.configuration.Configuration;
@ -67,7 +66,7 @@ public class TagModule extends BaseHandler implements TagService { @@ -67,7 +66,7 @@ public class TagModule extends BaseHandler implements TagService {
var user = userService().refreshSession(ex);
if (user.isEmpty()) return unauthorized(ex);
var module = path.pop();
if (module == null) throw unprocessable("Module missing in path.");
if (module == null) return getUserTags(ex, user.get());
var head = path.pop();
if (USES.equals(module)) return getTagUses(ex,head,user.get());
long entityId = Long.parseLong(head);
@ -79,10 +78,6 @@ public class TagModule extends BaseHandler implements TagService { @@ -79,10 +78,6 @@ public class TagModule extends BaseHandler implements TagService {
}
}
private boolean getTagUses(HttpExchange ex, String tag, UmbrellaUser user) throws IOException {
return sendContent(ex,tagDb.getUses(tag,user.id()));
}
@Override
public boolean doPost(Path path, HttpExchange ex) throws IOException {
addCors(ex);
@ -117,6 +112,10 @@ public class TagModule extends BaseHandler implements TagService { @@ -117,6 +112,10 @@ public class TagModule extends BaseHandler implements TagService {
}
}
private boolean getTagUses(HttpExchange ex, String tag, UmbrellaUser user) throws IOException {
return sendContent(ex,tagDb.getUses(tag,user.id()));
}
public Collection<String> getTags(String module, long entityId, UmbrellaUser user) throws UmbrellaException{
return tagDb.list(user.id(),module,entityId);
}
@ -126,6 +125,11 @@ public class TagModule extends BaseHandler implements TagService { @@ -126,6 +125,11 @@ public class TagModule extends BaseHandler implements TagService {
return tagDb.list(user.id(),module,entityIds);
}
private boolean getUserTags(HttpExchange ex, UmbrellaUser user) throws IOException {
var tuples = tagDb.list(user.id()).stream().filter(t -> t.a != null && t.b != null);
return sendContent(ex,tuples.map(t -> Map.of(TAG,t.a,COUNT,t.b)));
}
@Override
public void save(String module, long entityId, Collection<Long> userIds, Collection<String> tags) {
tagDb.save(userIds,module,entityId,tags);

1
translations/src/main/resources/de.json

@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
"contained_tax": "enthaltene Steuer",
"content": "Inhalt",
"context": "Kontext",
"count_of_occurrences": "{count} Verwendungen",
"country": "Land",
"COURT": "Amtsgericht",
"CUSTOMER-NUMBER": "Kundennummer",

9
web/src/main/resources/web/css/default.css

@ -423,4 +423,13 @@ fieldset.vcard{ @@ -423,4 +423,13 @@ fieldset.vcard{
.vcard td button.symbol{
float: right;
}
.cloud .tag{
border: 1px solid;
border-radius: 4px;
padding: 2px;
margin: 0 6px;
white-space: nowrap;
display: inline flow-root;
}
Loading…
Cancel
Save