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

knowledgepixels / nanodash / 20268226543

16 Dec 2025 12:39PM UTC coverage: 14.107% (-1.3%) from 15.358%
20268226543

push

github

ashleycaselli
refactor: replace VocabUtils with the ones defined in the nanopub-java library

526 of 4946 branches covered (10.63%)

Branch coverage included in aggregate %.

1452 of 9075 relevant lines covered (16.0%)

2.11 hits per line

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

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

3
import com.knowledgepixels.nanodash.template.Template;
4
import com.knowledgepixels.nanodash.template.TemplateData;
5
import com.knowledgepixels.nanodash.vocabulary.KPXL_TERMS;
6
import org.eclipse.rdf4j.model.IRI;
7
import org.eclipse.rdf4j.model.Literal;
8
import org.eclipse.rdf4j.model.Statement;
9
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
10
import org.nanopub.Nanopub;
11
import org.nanopub.extra.services.ApiResponse;
12
import org.nanopub.extra.services.ApiResponseEntry;
13
import org.nanopub.extra.services.QueryRef;
14
import org.nanopub.vocabulary.NTEMPLATE;
15

16
import java.io.Serializable;
17
import java.util.ArrayList;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Set;
21
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.concurrent.ConcurrentMap;
23

24
import static com.knowledgepixels.nanodash.Utils.vf;
25

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

31
    private static List<Project> projectList = null;
×
32
    private static ConcurrentMap<String, Project> projectsByCoreInfo = new ConcurrentHashMap<>();
×
33
    private static ConcurrentMap<String, Project> projectsById = new ConcurrentHashMap<>();
×
34

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

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

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

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

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

93
    private String id, label, rootNanopubId;
94
    private Nanopub rootNanopub = null;
×
95

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

106
    private boolean dataInitialized = false;
×
107
    private boolean dataNeedsUpdate = true;
×
108

109
    private Project(String id, String label, String rootNanopubId) {
×
110
        this.id = id;
×
111
        this.label = label;
×
112
        this.rootNanopubId = rootNanopubId;
×
113
        this.rootNanopub = Utils.getAsNanopub(rootNanopubId);
×
114

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

139
    }
×
140

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

305
}
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