• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 20752594734

06 Jan 2026 03:10PM UTC coverage: 14.869% (-0.3%) from 15.142%
20752594734

push

github

tkuhn
chore: Update CSS version link

569 of 5036 branches covered (11.3%)

Branch coverage included in aggregate %.

1552 of 9229 relevant lines covered (16.82%)

2.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.74
src/main/java/com/knowledgepixels/nanodash/Project.java
1
package com.knowledgepixels.nanodash;
2

3
import java.io.Serializable;
4
import java.util.ArrayList;
5
import java.util.HashSet;
6
import java.util.List;
7
import java.util.Set;
8
import java.util.concurrent.ConcurrentHashMap;
9
import java.util.concurrent.ConcurrentMap;
10

11
import org.eclipse.rdf4j.model.IRI;
12
import org.eclipse.rdf4j.model.Literal;
13
import org.eclipse.rdf4j.model.Statement;
14
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
15
import org.nanopub.Nanopub;
16
import org.nanopub.extra.services.ApiResponse;
17
import org.nanopub.extra.services.ApiResponseEntry;
18
import org.nanopub.extra.services.QueryRef;
19
import org.nanopub.vocabulary.NTEMPLATE;
20

21
import com.knowledgepixels.nanodash.template.Template;
22
import com.knowledgepixels.nanodash.template.TemplateData;
23
import com.knowledgepixels.nanodash.vocabulary.KPXL_TERMS;
24

25
/**
26
 * Class representing a Nanodash project.
27
 */
28
public class Project implements Serializable {
29

30
    private static List<Project> projectList = null;
2✔
31
    private static ConcurrentMap<String, Project> projectsByCoreInfo = new ConcurrentHashMap<>();
4✔
32
    private static ConcurrentMap<String, Project> projectsById = new ConcurrentHashMap<>();
5✔
33

34
    /**
35
     * Refresh the list of projects from the given API response.
36
     *
37
     * @param resp The API response containing project data.
38
     */
39
    public static synchronized void refresh(ApiResponse resp) {
40
        projectList = new ArrayList<>();
4✔
41
        ConcurrentMap<String, Project> prevProjectsByCoreInfoPrev = projectsByCoreInfo;
2✔
42
        projectsByCoreInfo = new ConcurrentHashMap<>();
4✔
43
        projectsById.clear();
2✔
44
        for (ApiResponseEntry entry : resp.getData()) {
11!
45
            Project project = new Project(entry.get("project"), entry.get("label"), entry.get("np"));
×
46
            Project prevProject = prevProjectsByCoreInfoPrev.get(project.getCoreInfoString());
×
47
            if (prevProject != null) project = prevProject;
×
48
            projectList.add(project);
×
49
            projectsByCoreInfo.put(project.getCoreInfoString(), project);
×
50
            projectsById.put(project.getId(), project);
×
51
        }
×
52
    }
×
53

54
    /**
55
     * Ensure that the project list is loaded. If not, it fetches the data from the API.
56
     */
57
    public static void ensureLoaded() {
58
        if (projectList == null) {
2!
59
            refresh(ApiCache.retrieveResponseSync(new QueryRef("get-projects"), true));
×
60
        }
61
    }
×
62

63
    /**
64
     * Get the list of all projects.
65
     */
66
    public static List<Project> getProjectList() {
67
        ensureLoaded();
×
68
        return projectList;
×
69
    }
70

71
    /**
72
     * Get a project by its ID.
73
     *
74
     * @param id The ID of the project.
75
     * @return The project with the given ID, or null if not found.
76
     */
77
    public static Project get(String id) {
78
        ensureLoaded();
×
79
        return projectsById.get(id);
×
80
    }
81

82
    /**
83
     * Mark all projects as needing data refresh.
84
     */
85
    public static void refresh() {
86
        ensureLoaded();
×
87
        for (Project project : projectList) {
×
88
            project.dataNeedsUpdate = true;
×
89
        }
×
90
    }
×
91

92
    private String id, label, rootNanopubId;
93
    private Nanopub rootNanopub = null;
3✔
94

95
    private String description = null;
3✔
96
    private List<IRI> owners = new ArrayList<>();
5✔
97
    private List<IRI> members = new ArrayList<>();
5✔
98
    private ConcurrentMap<String, IRI> ownerPubkeyMap = new ConcurrentHashMap<>();
5✔
99
    private List<Template> templates = new ArrayList<>();
5✔
100
    private Set<String> templateTags = new HashSet<>();
5✔
101
    private ConcurrentMap<String, List<Template>> templatesPerTag = new ConcurrentHashMap<>();
5✔
102
    private List<IRI> queryIds = new ArrayList<>();
5✔
103
    private IRI defaultProvenance = null;
3✔
104

105
    private boolean dataInitialized = false;
3✔
106
    private boolean dataNeedsUpdate = true;
3✔
107

108
    private Project(String id, String label, String rootNanopubId) {
2✔
109
        this.id = id;
3✔
110
        this.label = label;
3✔
111
        this.rootNanopubId = rootNanopubId;
3✔
112
        this.rootNanopub = Utils.getAsNanopub(rootNanopubId);
4✔
113

114
        for (Statement st : rootNanopub.getAssertion()) {
12!
115
            if (st.getSubject().stringValue().equals(getId())) {
7!
116
                if (st.getPredicate().equals(DCTERMS.DESCRIPTION)) {
5✔
117
                    description = st.getObject().stringValue();
6✔
118
                } else if (st.getPredicate().equals(KPXL_TERMS.HAS_OWNER) && st.getObject() instanceof IRI obj) {
14!
119
                    addOwner(obj);
4✔
120
                } else if (st.getPredicate().equals(KPXL_TERMS.HAS_PINNED_TEMPLATE) && st.getObject() instanceof IRI obj) {
14!
121
                    templates.add(TemplateData.get().getTemplate(obj.stringValue()));
9✔
122
                } else if (st.getPredicate().equals(KPXL_TERMS.HAS_PINNED_QUERY) && st.getObject() instanceof IRI obj) {
14!
123
                    queryIds.add(obj);
6✔
124
                } else if (st.getPredicate().equals(NTEMPLATE.HAS_DEFAULT_PROVENANCE) && st.getObject() instanceof IRI obj) {
5!
125
                    defaultProvenance = obj;
1✔
126
                }
127
            } else if (st.getPredicate().equals(NTEMPLATE.HAS_TAG) && st.getObject() instanceof Literal l) {
×
128
                templateTags.add(l.stringValue());
×
129
                List<Template> list = templatesPerTag.get(l.stringValue());
×
130
                if (list == null) {
×
131
                    list = new ArrayList<>();
×
132
                    templatesPerTag.put(l.stringValue(), list);
×
133
                }
134
                list.add(TemplateData.get().getTemplate(st.getSubject().stringValue()));
×
135
            }
136
        }
1✔
137

138
    }
×
139

140
    private void addOwner(IRI owner) {
141
        // TODO This isn't efficient for long owner lists:
142
        if (owners.contains(owner)) return;
5!
143
        owners.add(owner);
5✔
144
        UserData ud = User.getUserData();
2✔
145
        for (String pubkeyhash : ud.getPubkeyhashes(owner, true)) {
14✔
146
            ownerPubkeyMap.put(pubkeyhash, owner);
6✔
147
        }
1✔
148
    }
1✔
149

150
    /**
151
     * Get the ID of the project.
152
     *
153
     * @return The project ID.
154
     */
155
    public String getId() {
156
        return id;
3✔
157
    }
158

159
    /**
160
     * Get the root nanopublication ID of the project.
161
     *
162
     * @return The root nanopublication ID.
163
     */
164
    public String getRootNanopubId() {
165
        return rootNanopubId;
×
166
    }
167

168
    /**
169
     * Get a string containing the core information of the project (ID and root nanopub ID).
170
     *
171
     * @return A string with the project ID and root nanopub ID.
172
     */
173
    public String getCoreInfoString() {
174
        return id + " " + rootNanopubId;
×
175
    }
176

177
    /**
178
     * Get the root nanopublication of the project.
179
     *
180
     * @return The root nanopublication.
181
     */
182
    public Nanopub getRootNanopub() {
183
        return rootNanopub;
×
184
    }
185

186
    /**
187
     * Get the label of the project.
188
     *
189
     * @return The project label.
190
     */
191
    public String getLabel() {
192
        return label;
×
193
    }
194

195
    /**
196
     * Get the description of the project.
197
     *
198
     * @return The project description.
199
     */
200
    public String getDescription() {
201
        return description;
×
202
    }
203

204
    /**
205
     * Check if the project data has been initialized.
206
     *
207
     * @return True if the data is initialized, false otherwise.
208
     */
209
    public boolean isDataInitialized() {
210
        triggerDataUpdate();
×
211
        return dataInitialized;
×
212
    }
213

214
    /**
215
     * Get the list of owners of the project.
216
     *
217
     * @return A list of IRIs representing the owners.
218
     */
219
    public List<IRI> getOwners() {
220
        triggerDataUpdate();
×
221
        return owners;
×
222
    }
223

224
    /**
225
     * Get the list of members of the project.
226
     *
227
     * @return A list of IRIs representing the members.
228
     */
229
    public List<IRI> getMembers() {
230
        triggerDataUpdate();
×
231
        return members;
×
232
    }
233

234
    /**
235
     * Get the list of templates associated with the project.
236
     *
237
     * @return A list of templates.
238
     */
239
    public List<Template> getTemplates() {
240
        return templates;
×
241
    }
242

243
    /**
244
     * Get the set of template tags associated with the project.
245
     *
246
     * @return A set of template tags.
247
     */
248
    public Set<String> getTemplateTags() {
249
        return templateTags;
×
250
    }
251

252
    /**
253
     * Get a map of templates categorized by their tags.
254
     *
255
     * @return A concurrent map where keys are tags and values are lists of templates.
256
     */
257
    public ConcurrentMap<String, List<Template>> getTemplatesPerTag() {
258
        return templatesPerTag;
×
259
    }
260

261
    /**
262
     * Get the list of query IDs associated with the project.
263
     *
264
     * @return A list of IRIs representing the query IDs.
265
     */
266
    public List<IRI> getQueryIds() {
267
        return queryIds;
×
268
    }
269

270
    /**
271
     * Get the default provenance IRI for the project.
272
     *
273
     * @return The default provenance IRI.
274
     */
275
    public IRI getDefaultProvenance() {
276
        return defaultProvenance;
×
277
    }
278

279
    private synchronized void triggerDataUpdate() {
280
        if (dataNeedsUpdate) {
×
281
            new Thread(() -> {
×
282
                for (ApiResponseEntry r : ApiCache.retrieveResponseSync(new QueryRef("get-owners", "unit", id), true).getData()) {
×
283
                    String pubkeyhash = r.get("pubkeyhash");
×
284
                    if (ownerPubkeyMap.containsKey(pubkeyhash)) {
×
285
                        addOwner(Utils.vf.createIRI(r.get("owner")));
×
286
                    }
287
                }
×
288
                members = new ArrayList<>();
×
289
                for (ApiResponseEntry r : ApiCache.retrieveResponseSync(new QueryRef("get-members", "unit", id), true).getData()) {
×
290
                    IRI memberId = Utils.vf.createIRI(r.get("member"));
×
291
                    // TODO These checks are inefficient for long member lists:
292
                    if (owners.contains(memberId)) continue;
×
293
                    if (members.contains(memberId)) continue;
×
294
                    members.add(memberId);
×
295
                }
×
296
                owners.sort(User.getUserData().userComparator);
×
297
                members.sort(User.getUserData().userComparator);
×
298
                dataInitialized = true;
×
299
            }).start();
×
300
            dataNeedsUpdate = false;
×
301
        }
302
    }
×
303

304
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc