Skip to content

Commit a86f79d

Browse files
committed
Feat (API): package_version_diff API implemented
1 parent 9799746 commit a86f79d

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

ckanext/versions/logic/action.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import logging
2+
import json
3+
import difflib
24
from ckan.plugins import toolkit as tk
35
from ckanext.versions.logic.schema import package_version_create, package_version_update
46
from ckanext.versions.model import DatasetVersion
@@ -219,3 +221,81 @@ def package_version_exists(context, data_dict):
219221
return {"exists": False}
220222
return {"exists": True}
221223

224+
225+
226+
227+
228+
@tk.side_effect_free
229+
def package_version_diff(context, data_dict):
230+
"""Returns a diff of the version, compared to the previous version of the
231+
object
232+
233+
:param id: the id of the version
234+
:type id: string
235+
:param diff_type: 'unified', 'context', 'html'
236+
:type diff_type: string
237+
"""
238+
239+
model = context["model"]
240+
version_id = tk.get_or_bust(data_dict, "id")
241+
diff_type = data_dict.get("diff_type", "unified")
242+
243+
tk.check_access("package_version_diff", context, data_dict)
244+
245+
version = DatasetVersion.get(id=version_id)
246+
if version is None:
247+
raise tk.ObjectNotFound()
248+
prev_version = (
249+
model.Session.query(DatasetVersion)
250+
.filter(DatasetVersion.package_id == version.package_id) # Filter by package_id
251+
.filter(DatasetVersion.created < version.created) # Ensure it's an earlier version
252+
.order_by(DatasetVersion.created.desc()) # Order by creation date descending
253+
.first()
254+
)
255+
print("latest", version)
256+
print("preiv", prev_version)
257+
258+
if prev_version is None:
259+
raise tk.ObjectNotFound("Previous version for this object not found")
260+
261+
version_list = [prev_version, version]
262+
263+
try:
264+
version_list = [
265+
vers.data for vers in version_list
266+
]
267+
except KeyError:
268+
raise tk.ObjectNotFound("Could not find object in the version data")
269+
# convert each object dict to 'pprint'-style
270+
# and split into lines to suit difflib
271+
obj_lines = [
272+
json.dumps(ver, indent=2, sort_keys=True).split("\n") for ver in version_list
273+
]
274+
275+
# do the diff
276+
if diff_type == "unified":
277+
# type_ignore_reason: typechecker can't predict number of items
278+
diff_generator = difflib.unified_diff(*obj_lines) # type: ignore
279+
diff = "\n".join(line for line in diff_generator)
280+
elif diff_type == "context":
281+
# type_ignore_reason: typechecker can't predict number of items
282+
diff_generator = difflib.context_diff(*obj_lines) # type: ignore
283+
diff = "\n".join(line for line in diff_generator)
284+
elif diff_type == "html":
285+
# word-wrap lines. Otherwise you get scroll bars for most datasets.
286+
import re
287+
288+
for obj_index in (0, 1):
289+
wrapped_obj_lines = []
290+
for line in obj_lines[obj_index]:
291+
wrapped_obj_lines.extend(re.findall(r".{1,70}(?:\s+|$)", line))
292+
obj_lines[obj_index] = wrapped_obj_lines
293+
# type_ignore_reason: typechecker can't predict number of items
294+
diff = difflib.HtmlDiff().make_table(*obj_lines) # type: ignore
295+
else:
296+
raise tk.ValidationError({"message": "diff_type not recognized"})
297+
298+
299+
return {
300+
"diff": diff,
301+
}

ckanext/versions/logic/auth.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,12 @@ def package_version_delete(context, data_dict):
4242
This is permitted only to users who are allowed to modify the dataset
4343
"""
4444
return is_authorized('package_delete', context,
45+
{"id": data_dict['package_id']})
46+
47+
def package_version_diff(context, data_dict):
48+
"""Check if a user is allowed to view the diff of a version
49+
50+
This is permitted only to users who are allowed to view the dataset
51+
"""
52+
return is_authorized('package_show', context,
4553
{"id": data_dict['package_id']})

ckanext/versions/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def get_actions(self):
3636
"package_version_list": action.package_version_list,
3737
"package_version_delete": action.package_version_delete,
3838
"package_version_exists": action.package_version_exists,
39+
"package_version_diff": action.package_version_diff,
3940
}
4041

4142
# IAuthFunctions
@@ -46,6 +47,7 @@ def get_auth_functions(self):
4647
"package_version_update": auth.package_version_update,
4748
"package_version_list": auth.package_version_list,
4849
"package_version_delete": auth.package_version_delete,
50+
"package_version_diff": auth.package_version_diff,
4951
}
5052

5153
# IValidators

0 commit comments

Comments
 (0)