preparing to manage weights in poll

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-02-25 09:17:25 +01:00
parent cbc6ce188d
commit 4a2c49c42c
6 changed files with 86 additions and 14 deletions

View File

@@ -51,4 +51,5 @@ public class Path {
public static final String USER = "user"; public static final String USER = "user";
public static final String USES = "uses"; public static final String USES = "uses";
public static final String WEIGHT = "weight";
} }

View File

@@ -76,6 +76,7 @@ public class Text {
public static final String UNIT = "unit"; public static final String UNIT = "unit";
public static final String USER_WITH_ID = "user ({id})"; public static final String USER_WITH_ID = "user ({id})";
public static final String WEIGHT = "weight";
public static final String WIKI = "wiki"; public static final String WIKI = "wiki";
public static final String WIKI_PAGE = "wiki page"; public static final String WIKI_PAGE = "wiki page";
public static final String WIKI_PAGES = "wiki pages"; public static final String WIKI_PAGES = "wiki pages";

View File

@@ -15,12 +15,12 @@ import static java.text.MessageFormat.format;
public class Poll implements Mappable { public class Poll implements Mappable {
public static class Option implements Mappable{ public static class Option implements Mappable{
private int id; private int id;
Integer status; Integer status;
private String description; private String description;
private String name; private String name;
private final Set<String> dirtyFields = new HashSet<>(); private final Set<String> dirtyFields = new HashSet<>();
public Option(int id, String name, String description, Integer status) { public Option(int id, String name, String description, Integer status) {
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -102,16 +102,16 @@ public class Poll implements Mappable {
public String toString() { public String toString() {
return format("Option \"{0}\"",name); return format("Option \"{0}\"",name);
} }
}
}
private Owner owner; private Owner owner;
private String id, name, description; private String id, name, description;
private boolean isPrivate; private boolean isPrivate;
private List<Option> options; private List<Option> options;
private Map<Integer,String> weights; private Map<Integer,String> weights;
private Map<UmbrellaUser,Long> shares; private Map<UmbrellaUser,Long> shares;
private Set<String> dirtyFields = new HashSet<>(); private Set<String> dirtyFields = new HashSet<>();
public Poll(String id, Owner owner, String name, String description, boolean isPrivate, List<Option> options, Map<Integer,String> weights, Map<UmbrellaUser,Long> shares){ public Poll(String id, Owner owner, String name, String description, boolean isPrivate, List<Option> options, Map<Integer,String> weights, Map<UmbrellaUser,Long> shares){
this.id = id; this.id = id;
this.owner = owner; this.owner = owner;
@@ -141,6 +141,13 @@ public class Poll implements Mappable {
return !dirtyFields.isEmpty(); return !dirtyFields.isEmpty();
} }
public boolean isDirty(String ... fields){
for (var field : fields){
if (dirtyFields.contains(field)) return true;
}
return false;
}
public boolean isPrivate(){ public boolean isPrivate(){
return isPrivate; return isPrivate;
} }
@@ -180,10 +187,17 @@ public class Poll implements Mappable {
public List<Option> options(){ public List<Option> options(){
return options; return options;
} }
public Owner owner(){ public Owner owner(){
return owner; return owner;
} }
public Poll setWeight(Integer weight, String description) {
weights.put(weight,description);
dirtyFields.add(WEIGHTS);
return this;
}
public Map<UmbrellaUser, Long> shares(){ public Map<UmbrellaUser, Long> shares(){
return shares; return shares;
} }

View File

@@ -12,6 +12,7 @@
let { id } = $props(); let { id } = $props();
let new_option = $state({name:'',description:{'source':'',rendered:''}}); let new_option = $state({name:'',description:{'source':'',rendered:''}});
let new_weight = $state({description:'',weight:0});
let poll = $state(null); let poll = $state(null);
async function load(){ async function load(){
@@ -51,7 +52,23 @@
if (field == 'name' && newVal == ''){ if (field == 'name' && newVal == ''){
poll.options = poll.options.filter(o => o.id !== option.id); poll.options = poll.options.filter(o => o.id !== option.id);
} else poll.options = json.options; } else poll.options = json.options;
} else error(res); return true;
}
error(res);
return false;
}
async function patch_weight(data){
let url = api(`poll/${id}/weight`);
let res = await patch(url,data);
if (res.ok) {
yikes();
const json = await res.json();
console.log(json);
return true;
}
error(res);
return false;
} }
@@ -66,6 +83,12 @@
} else error(res); } else error(res);
} }
function save_new_weight(e){
const data = {};
data[new_weight.weight] = new_weight.description;
patch_weight(data);
}
onMount(load); onMount(load);
</script> </script>
@@ -130,10 +153,19 @@
<input type="number" value={weight} /> <input type="number" value={weight} />
</td> </td>
<td> <td>
<input type="text" value={descr} /> <LineEditor value={descr} onSet={desc => patch_weight({weight: desc})} />
</td> </td>
</tr> </tr>
{/each} {/each}
<tr>
<td>
<input type="number" bind:value={new_weight.weight} />
</td>
<td>
<input type="text" bind:value={new_weight.description} />
<button onclick={save_new_weight}>{t('save')}</button>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</fieldset> </fieldset>

View File

@@ -5,8 +5,7 @@ import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService; import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.constants.Field.FIELD; import static de.srsoftware.umbrella.core.constants.Field.FIELD;
import static de.srsoftware.umbrella.core.constants.Field.ID; import static de.srsoftware.umbrella.core.constants.Field.ID;
import static de.srsoftware.umbrella.core.constants.Path.LIST; import static de.srsoftware.umbrella.core.constants.Path.*;
import static de.srsoftware.umbrella.core.constants.Path.OPTION;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE; import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE;
import static java.lang.System.Logger.Level.WARNING; import static java.lang.System.Logger.Level.WARNING;
@@ -117,6 +116,7 @@ public class PollModule extends BaseHandler implements PollService {
return switch (head){ return switch (head){
case null -> patchPoll(ex, poll); case null -> patchPoll(ex, poll);
case OPTION -> patchPollOptions(ex, path, poll); case OPTION -> patchPollOptions(ex, path, poll);
case WEIGHT -> patchWeights(ex, poll);
default -> notFound(ex); default -> notFound(ex);
}; };
} }
@@ -136,6 +136,11 @@ public class PollModule extends BaseHandler implements PollService {
return sendContent(ex,pollDb.save(poll)); return sendContent(ex,pollDb.save(poll));
} }
private boolean patchWeights(HttpExchange ex, Poll poll) throws IOException {
// TODO
return notFound(ex);
}
private boolean patchPollOptions(HttpExchange ex, Path path, Poll poll) throws IOException { private boolean patchPollOptions(HttpExchange ex, Path path, Poll poll) throws IOException {
try { try {
if (path.empty()) throw missingField(ID); if (path.empty()) throw missingField(ID);
@@ -180,7 +185,7 @@ public class PollModule extends BaseHandler implements PollService {
var head = path.pop(); var head = path.pop();
return switch (head){ return switch (head){
case OPTION -> postOption(ex, poll); case OPTION -> postOption(ex, poll);
default -> notFound(ex); case null, default -> notFound(ex);
}; };
} }

View File

@@ -10,13 +10,11 @@ import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Field.DESCRIPTION; import static de.srsoftware.umbrella.core.constants.Field.DESCRIPTION;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.poll.Constants.*; import static de.srsoftware.umbrella.poll.Constants.*;
import static java.lang.System.Logger.Level.WARNING;
import static java.text.MessageFormat.format; import static java.text.MessageFormat.format;
import de.srsoftware.umbrella.core.BaseDb; import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.constants.Field; import de.srsoftware.umbrella.core.constants.Field;
import de.srsoftware.umbrella.core.constants.Text; import de.srsoftware.umbrella.core.constants.Text;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Poll; import de.srsoftware.umbrella.core.model.Poll;
import de.srsoftware.umbrella.core.model.UmbrellaUser; import de.srsoftware.umbrella.core.model.UmbrellaUser;
@@ -112,6 +110,15 @@ public class SqliteDb extends BaseDb implements PollDb {
} }
} }
private void dropWeight(Poll poll, int weight){
try {
delete().from(TABLE_WEIGHTS).where(POLL_ID, equal(poll.id())).where(ID, equal(weight)).execute(db);
poll.weights().remove(weight);
} catch (SQLException e){
throw failedToDropObject(Text.WEIGHT);
}
}
@Override @Override
public Collection<Poll> listPolls(UmbrellaUser user) { public Collection<Poll> listPolls(UmbrellaUser user) {
try { try {
@@ -145,7 +152,7 @@ public class SqliteDb extends BaseDb implements PollDb {
while (rs.next()) poll.options().add(Poll.Option.of(rs)); while (rs.next()) poll.options().add(Poll.Option.of(rs));
rs.close(); rs.close();
rs = select(ALL).from(TABLE_WEIGHTS).where(POLL_ID,equal(id)).exec(db); rs = select(ALL).from(TABLE_WEIGHTS).where(POLL_ID,equal(id)).sort(WEIGHT+" ASC").exec(db);
while (rs.next()) { while (rs.next()) {
var weight = rs.getInt(WEIGHT); var weight = rs.getInt(WEIGHT);
var descr = rs.getString(DESCRIPTION); var descr = rs.getString(DESCRIPTION);
@@ -200,13 +207,25 @@ public class SqliteDb extends BaseDb implements PollDb {
} }
private Poll update(Poll poll) { private Poll update(Poll poll) {
if (poll.isDirty()) try { if (poll.isDirty(NAME, DESCRIPTION)) try {
LOG.log(WARNING,"Updating poll not fully implemented");
replaceInto(TABLE_POLLS,ID,Field.USER_ID,NAME,DESCRIPTION).values(poll.id(),poll.owner().id(),poll.name(),poll.description()).execute(db); replaceInto(TABLE_POLLS,ID,Field.USER_ID,NAME,DESCRIPTION).values(poll.id(),poll.owner().id(),poll.name(),poll.description()).execute(db);
} catch (SQLException e){ } catch (SQLException e){
throw failedToStoreObject(poll); throw failedToStoreObject(poll);
} }
if (poll.isDirty(WEIGHTS)) try {
Map.copyOf(poll.weights())
.entrySet().stream()
.filter(entry -> entry.getValue().isBlank())
.map(Map.Entry::getKey)
.forEach(w -> dropWeight(poll,w));
var query = replaceInto(TABLE_WEIGHTS, POLL_ID, WEIGHT, DESCRIPTION);
for (var entry : poll.weights().entrySet()){
query.values(poll.id(),entry.getKey(),entry.getValue());
}
query.execute(db).close();
} catch (SQLException e) {
throw failedToStoreObject(poll);
}
for (var option : poll.options()) saveOption(poll.id(),option); for (var option : poll.options()) saveOption(poll.id(),option);
return poll; return poll;
} }