Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support having one json-file per content item #223

Merged
merged 3 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ pip =
[versions:python27]
# Last pyrsistent version that is python 2 compatible:
pyrsistent = 0.15.7
pathlib2 = 2.3.6
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# which has a Py3-only release since September 2021.
install_requires.append("jsonschema < 4")
install_requires.append("pyrsistent < 0.16.0")
install_requires.append("pathlib2")
else:
install_requires.append("plone.restapi")
install_requires.append("beautifulsoup4")
Expand Down
47 changes: 46 additions & 1 deletion src/collective/exportimport/export_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,50 @@ def __call__(
content_generator = self.export_content()

number = 0
if download_to_server:

# Export each item to a separate json-file
if download_to_server == 2:
directory = config.CENTRAL_DIRECTORY
if directory:
if not os.path.exists(directory):
os.makedirs(directory)
logger.info("Created central export/import directory %s", directory)
else:
cfg = getConfiguration()
directory = cfg.clienthome

# Use the filename (Plone.json) as target for files (Plone/1.json)
directory = os.path.join(directory, filename[:-5])
if not os.path.exists(directory):
os.makedirs(directory)
logger.info("Created directory to hold content: %s", directory)

self.start()
for number, datum in enumerate(content_generator, start=1):
filename = "{}.json".format(number)
filepath = os.path.join(directory, filename)
with open(filepath, "w") as f:
json.dump(datum, f, sort_keys=True, indent=4)
if number:
if self.errors and self.write_errors:
errors = {"unexported_paths": self.errors}
json.dump(errors, f, indent=4)
msg = _(u"Exported {} items ({}) to {} with {} errors").format(
number, ", ".join(self.portal_type), directory, len(self.errors)
)
logger.info(msg)
api.portal.show_message(msg, self.request)

if self.include_blobs == 1:
# remove marker interface
noLongerProvides(self.request, IBase64BlobsMarker)
elif self.include_blobs == 2:
noLongerProvides(self.request, IPathBlobsMarker)
self.finish()
self.request.response.redirect(self.request["ACTUAL_URL"])

# Export all items into one json-file in the filesystem
elif download_to_server:
directory = config.CENTRAL_DIRECTORY
if directory:
if not os.path.exists(directory):
Expand Down Expand Up @@ -218,6 +261,8 @@ def __call__(
noLongerProvides(self.request, IPathBlobsMarker)
self.finish()
self.request.response.redirect(self.request["ACTUAL_URL"])

# Export as one json-file through the browser
else:
with tempfile.TemporaryFile(mode="w+") as f:
self.start()
Expand Down
54 changes: 53 additions & 1 deletion src/collective/exportimport/import_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
except ImportError:
HAS_COLLECTION_FIX = False

if six.PY2:
from pathlib2 import Path
else:
from pathlib import Path

logger = logging.getLogger(__name__)
BLOB_HOME = os.getenv("COLLECTIVE_EXPORTIMPORT_BLOB_HOME", "")

Expand Down Expand Up @@ -73,6 +78,31 @@ def get_absolute_blob_path(obj, blob_path):
return abs_path


def filesystem_walker(path=None):
root = Path(path)
assert(root.is_dir())

# first import json-files directly in the path
json_files = [i for i in root.glob("*.json") if i.stem.isdecimal()]
for json_file in sorted(json_files, key=lambda i: int(i.stem)):
logger.debug("Importing %s", json_file)
item = json.loads(json_file.read_text())
item["json_file"] = str(json_file)
if item:
yield item

# then import json-files of any containing folders
folders = [i for i in root.iterdir() if i.is_dir() and i.name.isdecimal()]
for folder in sorted(folders, key=lambda i: int(i.name)):
json_files = [i for i in folder.glob("*.json") if i.stem.isdecimal()]
for json_file in sorted(json_files, key=lambda i: int(i.stem)):
logger.debug("Importing %s", json_file)
item = json.loads(json_file.read_text())
item["json_file"] = str(json_file)
if item:
yield item


class ImportContent(BrowserView):

template = ViewPageTemplateFile("templates/import_content.pt")
Expand Down Expand Up @@ -112,7 +142,8 @@ def __call__(
return_json=False,
limit=None,
server_file=None,
iterator=None
iterator=None,
server_directory=False,
):
request = self.request
self.limit = limit
Expand Down Expand Up @@ -192,6 +223,11 @@ def __call__(
msg = self.do_import(iterator)
api.portal.show_message(msg, self.request)

if server_directory:
self.start()
msg = self.do_import(filesystem_walker(server_directory))
api.portal.show_message(msg, self.request)

self.finish()

if return_json:
Expand Down Expand Up @@ -243,6 +279,22 @@ def server_files(self):
listing.sort()
return listing

@property
def server_directories(self):
# Adapted from ObjectManager.list_imports, which lists zexps.
listing = []
for directory in self.import_paths:
if not os.path.isdir(directory):
continue
# import pdb; pdb.set_trace()
listing += [
os.path.join(directory, f)
for f in os.listdir(directory)
if os.path.isdir(os.path.join(directory, f)) and f not in listing
]
listing.sort()
return listing

def do_import(self, data):
start = datetime.now()
alsoProvides(self.request, IMigrationMarker)
Expand Down
6 changes: 6 additions & 0 deletions src/collective/exportimport/templates/export_content.pt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@
Save to file on server
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="download_to_server:int" value="2" id="separate_files">
<label for="separate_files" class="form-check-label" i18n:translate="">
Save each item as a separate file on the server
</label>
</div>
</div>


Expand Down
28 changes: 22 additions & 6 deletions src/collective/exportimport/templates/import_content.pt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
<input type="file" name="jsonfile"/><br/>
</div>

<p>You can also select a json-file or a directory holding json-files on the server in the following locations:</p>
<ul>
<li tal:repeat="path view/import_paths"><code tal:content="path" /></li>
</ul>

<tal:block define="server_files view/server_files">
<p i18n:translate="server_paths_list">Or you can choose a file that is already uploaded on the server in one of these paths:</p>
<ul>
<li tal:repeat="path view/import_paths"><code tal:content="path" /></li>
</ul>
<p tal:condition="not:server_files" i18n:translate="">No files found.</p>
<p i18n:translate="server_paths_list">Import from a json-file</p>
<p tal:condition="not:server_files" i18n:translate="">No json-files found.</p>
<div class="field mb-3" tal:condition="server_files">
<label for="server_file" i18n:translate="">File on server to import:</label>
<label for="server_file" i18n:translate="">json-file on server to import:</label>
<br />
<select id="server_file" name="server_file">
<option selected="" value="" title="" i18n:translate="">Choose one</option>
Expand All @@ -34,6 +36,20 @@
</div>
</tal:block>

<tal:block define="server_directories view/server_directories">
<p i18n:translate="server_paths_list">Import from a directory that holds individual json-files per item:</p>
<p tal:condition="not:server_directories" i18n:translate="">No directories to import from found.</p>
<div class="field mb-3" tal:condition="server_directories">
<label for="server_directory" i18n:translate="">Directory on server to import:</label>
<br />
<select id="server_directory" name="server_directory">
<option selected="" value="" title="" i18n:translate="">Choose one</option>
<option tal:repeat="directoryname server_directories" tal:content="directoryname" tal:attributes="value directoryname">
</option>
</select>
</div>
</tal:block>

<div class="field mb-3">
<label for="include_blobs" i18n:translate="">Handle existing content</label>
<span class="formHelp" i18n:translate="">
Expand Down