Skip to content

Commit

Permalink
Fix observing list import/export (#3207)
Browse files Browse the repository at this point in the history
ObsList fixes
- Fix list export (Fix #3206)
- Add last-edited date
- display newly-loaded list (last when loading multiple lists)
- use system time, not simulation time, for list timestamping
- export single .sol and complete .ol lists.
- import single .sol, complete .ol or .json lists.
- SUG: Update about ObsLists
  • Loading branch information
gzotti authored Sep 12, 2023
1 parent dec1204 commit ff1df3b
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 46 deletions.
15 changes: 8 additions & 7 deletions guide/ch_tour.tex
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,7 @@ \section{Taking Screenshots}
\section{Observing Lists (Bookmarks)}
\label{sec:tour:bookmarks}

You can store your favourite objects or views in observing lists. This
feature extends the earlier bookmarks feature.

You can store your favourite objects or views in observing lists.
Press \key{Alt+B} or \guibutton[0.75]{2.5}{bt_bookmarks.png} to call
up the dialog. A bookmarks file from previous versions will be
imported and converted to the new format. You can however create an
Expand All @@ -319,14 +317,17 @@ \section{Observing Lists (Bookmarks)}
to restore. Additionally, the field of view may be relevant. To store
these data and later retrieve them, use the respective checkboxes.

You can also export the current list with the according
button, or import such an exported list on another system. Lists are identified with an
You can also export the current list or all lists with the according
button, or import such an exported list on another system.
To export the current list, keep the default \texttt{.sol} file type in the filename dialog.
To export all your lists, use the \texttt{.ol} file type in the filename dialog.
Lists are identified with an
Universally Unique Identifier (UUID) which we call OLUD (Observing List Unique Identifier).
On importing a list, existing lists with the same OLUD may be replaced after a warning.
During editing, storing a list with an existing name is prevented.
The list import may however import a list with an existing name as long as the OLUD is different.
To transfer all your lists to another system, copy, rename and import
(or just copy, to replace the existing) the full file \file{observingLists.json}
To transfer all your lists to another system, export them as \texttt{.ol} file, or
copy, rename and import (or just copy, to replace the existing) the full file \file{observingLists.json}
from the \file{data/} subdirectory of your user directory (see section~\ref{sec:Directories}).


Expand Down
98 changes: 61 additions & 37 deletions src/gui/ObsListDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,8 @@ void ObsListDialog::createDialogContent()
ui->treeView->setModel(itemModel);
ui->treeView->header()->setSectionsMovable(false);
ui->treeView->hideColumn(ColumnUUID);
ui->treeView->header()->setSectionResizeMode(ColumnDesignation, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnNameI18n, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnType, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnRa, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnDec, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnMagnitude, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnConstellation, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnDate, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnLocation, QHeaderView::ResizeToContents);
ui->treeView->header()->setSectionResizeMode(ColumnLandscapeID, QHeaderView::ResizeToContents);
for (int c=ColumnDesignation; c<=ColumnLandscapeID; c++)
ui->treeView->header()->setSectionResizeMode(c, QHeaderView::ResizeToContents);
ui->treeView->header()->setStretchLastSection(true);
//Enable the sort for columns
ui->treeView->setSortingEnabled(true);
Expand All @@ -177,14 +169,15 @@ void ObsListDialog::createDialogContent()
observingLists = QMap<QString, QVariant>();
// Create one default empty list
// Creation date
const double JD = core->getJD();
const double JD = StelUtils::getJDFromSystem();
const QString listCreationDate = StelUtils::julianDayToISO8601String(JD + core->getUTCOffset(JD) / 24.).replace("T", " ");
QVariantMap emptyList = {
// Name, description, current date for the list, current sorting
{KEY_NAME, qc_("new list", "default name for observing list if none is available")},
{KEY_DESCRIPTION, QString()},
{KEY_SORTING, QString()},
{KEY_CREATION_DATE, listCreationDate }};
{KEY_CREATION_DATE, listCreationDate },
{KEY_LAST_EDIT, listCreationDate }};

observingLists.insert(olud, emptyList);
jsonMap.insert(KEY_OBSERVING_LISTS, observingLists);
Expand Down Expand Up @@ -432,6 +425,7 @@ void ObsListDialog::loadSelectedList()
ui->listNameLineEdit->setText(currentListName);
ui->descriptionLineEdit->setText(observingListMap.value(KEY_DESCRIPTION).toString());
ui->creationDateLineEdit->setText(observingListMap.value(KEY_CREATION_DATE).toString());
ui->lastEditLineEdit->setText(observingListMap.value(KEY_LAST_EDIT, observingListMap.value(KEY_CREATION_DATE)).toString());

if (observingListMap.value(KEY_OBJECTS).canConvert<QVariantList>())
{
Expand Down Expand Up @@ -662,7 +656,7 @@ QHash<QString, ObsListDialog::observingListItem> ObsListDialog::loadBookmarksFil
/*
* Save the bookmarks into observingLists QVariantMap
*/
void ObsListDialog::saveBookmarksHashInObservingLists(const QHash<QString, observingListItem> &bookmarksHash)
QString ObsListDialog::saveBookmarksHashInObservingLists(const QHash<QString, observingListItem> &bookmarksHash)
{
// Creation date
double JD = StelUtils::getJDFromSystem(); // Mark with current system time
Expand All @@ -672,6 +666,7 @@ void ObsListDialog::saveBookmarksHashInObservingLists(const QHash<QString, obser
{KEY_NAME, BOOKMARKS_LIST_NAME},
{KEY_DESCRIPTION, BOOKMARKS_LIST_DESCRIPTION},
{KEY_CREATION_DATE, listCreationDate},
{KEY_LAST_EDIT, listCreationDate},
{KEY_SORTING, SORTING_BY_NAME}};

// Add actual list of (former) bookmark entries
Expand All @@ -689,6 +684,7 @@ void ObsListDialog::saveBookmarksHashInObservingLists(const QHash<QString, obser
QString bookmarkListOlud= (keys.empty() ? QUuid::createUuid().toString() : keys.at(0));

observingLists.insert(bookmarkListOlud, bookmarksObsList);
return bookmarkListOlud;
}

/*
Expand Down Expand Up @@ -907,9 +903,11 @@ void ObsListDialog::editListButtonPressed()
*/
void ObsListDialog::exportListButtonPressed()
{
static const QString filter = "JSON (*.json)";
static const QString filter = "Stellarium Single Observing List (*.sol);;Stellarium Observing List (*.ol)";
QString selectedFilter = "Stellarium Single Observing List (*.sol)";
QString exportListJsonPath = QFileDialog::getSaveFileName(nullptr, q_("Export observing list as..."),
QDir::homePath() + "/" + JSON_FILE_BASENAME + "_" + currentListName + ".json", filter);
QDir::homePath() + "/" + JSON_FILE_BASENAME + "_" + currentListName + ".sol", filter, &selectedFilter);

QFile jsonFile(exportListJsonPath);
if (!jsonFile.open(QIODevice::ReadWrite | QIODevice::Text))
{
Expand All @@ -919,18 +917,29 @@ void ObsListDialog::exportListButtonPressed()
messageBox(q_("Error"), q_("Cannot export. See logfile for details."));
return;
}
// Prepare a new json-able map
QVariantMap exportJsonMap={
{KEY_DEFAULT_LIST_OLUD, ""}, // Do not set a default in this list!
{KEY_SHORT_NAME, SHORT_NAME_VALUE},
{KEY_VERSION, FILE_VERSION}};

QVariantMap currentListMap={{selectedOlud, observingLists.value(selectedOlud).toMap()}};
QVariantMap oneListMap={{KEY_OBSERVING_LISTS, currentListMap}};
exportJsonMap.insert(KEY_OBSERVING_LISTS, oneListMap);

jsonFile.resize(0);
StelJsonParser::write(exportJsonMap, &jsonFile);
QFileInfo fi(exportListJsonPath);
if (fi.suffix()=="sol")
{
// Prepare a new json-able map
QVariantMap exportJsonMap={
{KEY_DEFAULT_LIST_OLUD, ""}, // Do not set a default in this list!
{KEY_SHORT_NAME, SHORT_NAME_VALUE},
{KEY_VERSION, FILE_VERSION}};

QVariantMap currentListMap={{selectedOlud, observingLists.value(selectedOlud).toMap()}};
//QVariantMap oneListMap={{KEY_OBSERVING_LISTS, currentListMap}};
//exportJsonMap.insert(KEY_OBSERVING_LISTS, oneListMap);
exportJsonMap.insert(KEY_OBSERVING_LISTS, currentListMap);
StelJsonParser::write(exportJsonMap, &jsonFile);
}
else
{
// just export complete map.
StelJsonParser::write(jsonMap, &jsonFile);
}

jsonFile.flush();
jsonFile.close();
}
Expand All @@ -940,7 +949,7 @@ void ObsListDialog::exportListButtonPressed()
*/
void ObsListDialog::importListButtonPressed()
{
static const QString filter = "JSON (*.json)";
static const QString filter = "Stellarium Single Observing List (*.sol);;Stellarium Observing List (*.ol);;Stellarium Legacy JSON Observing List or Bookmarks (*.json)";
QString fileToImportJsonPath = QFileDialog::getOpenFileName(nullptr, q_("Import observing list"),
QDir::homePath(),
filter);
Expand All @@ -961,7 +970,7 @@ void ObsListDialog::importListButtonPressed()

if (map.contains(KEY_OBSERVING_LISTS))
{
// Case of observingList import: Import all lists from that file!
// Case of observingList import: Import all lists from that file! A .sol only has one list but is else structured identically.
const QVariantMap observingListMapToImport = map.value(KEY_OBSERVING_LISTS).toMap();
if (observingListMapToImport.isEmpty())
{
Expand All @@ -977,19 +986,25 @@ void ObsListDialog::importListButtonPressed()
{
// check here to avoid overwriting of existing lists
bool overwrite=true;
if (observingLists.contains(it.key()))
if (observingLists.contains(it.key())) // Same OLUD?
{
QVariantMap importedMap=it.value().toMap();
QVariantMap importedMap=it.value().toMap(); // This is a map of {{UUID, QMap},...}
QString importedName=importedMap.value(KEY_NAME).toString();
QString importedDate=importedMap.value(KEY_CREATION_DATE).toString();
QString importedLastEditDate=importedMap.value(KEY_LAST_EDIT, importedMap.value(KEY_CREATION_DATE)).toString();
qDebug() << "Imported Map named:" << importedName << "created" << importedDate << "changed" << importedLastEditDate << ":" << importedMap;
QVariantMap existingMap=observingLists.value(it.key()).toMap();
QString existingName=existingMap.value(KEY_NAME).toString();
QString existingDate=existingMap.value(KEY_CREATION_DATE).toString();
QString message=QString(q_("A list named '%1' and dated %2 would overwrite your existing list '%3', dated %4. Accept?")).arg(importedName, importedDate, existingName, existingDate);
QString existingLastEdit=existingMap.value(KEY_LAST_EDIT, existingMap.value(KEY_CREATION_DATE)).toString();
QString message=QString(q_("A list named '%1', created %2 and last modified %3 would overwrite your existing list '%4', dated %5 and last modified %6. Accept?")).arg(importedName, importedDate, importedLastEditDate, existingName, existingDate, existingLastEdit);
overwrite=askConfirmation(message);
}
if (overwrite)
{
observingLists.insert(it.key(), it.value());
selectedOlud=it.key();
}
}
}
}
Expand All @@ -1008,7 +1023,7 @@ void ObsListDialog::importListButtonPressed()
{
QHash<QString, ObsListDialog::observingListItem> bookmarksHash=loadBookmarksFile(jsonFile);
// Put them to the main list. Note that this may create another list named "bookmarks list", however, with a different OLUD than the existing.
saveBookmarksHashInObservingLists(bookmarksHash);
selectedOlud=saveBookmarksHashInObservingLists(bookmarksHash);
}
}
else
Expand All @@ -1032,6 +1047,10 @@ void ObsListDialog::importListButtonPressed()
jsonFile.flush();
jsonFile.close();
tainted=false;

// Now we have stored to file, but the program is not aware of the new lists!
loadListNames(); // also populate Combobox and make sure at least some defaultOlud exists.
loadSelectedList();
} catch (std::runtime_error &e) {
qWarning() << "[ObservingList Creation/Edition] File format is wrong! Error: " << e.what();
messageBox(q_("Error"), q_("File format is wrong!"));
Expand Down Expand Up @@ -1094,7 +1113,7 @@ void ObsListDialog::addObjectButtonPressed()

if (!selectedObject.isEmpty())
{
// TBD: this test should prevent adding duplicate entries, but fails. Maybe for V23.2!
// TBD: this test should prevent adding duplicate entries, but fails. Maybe for V23.4!
// // No duplicate item in the same list
// bool is_already_in_list = false;
// QHash<QString, observingListItem>::iterator i;
Expand Down Expand Up @@ -1301,6 +1320,8 @@ void ObsListDialog::switchEditMode(bool enableEditMode, bool newList)

ui->creationDateLabel->setVisible(!isEditMode); // Creation date:
ui->creationDateLineEdit->setVisible(!isEditMode); //
ui->lastEditLabel->setVisible(!isEditMode); // Last edit date:
ui->lastEditLineEdit->setVisible(!isEditMode); //

// line with optional store items
ui->alsoStoreLabel->setVisible(isEditMode); // Also store
Expand Down Expand Up @@ -1374,19 +1395,21 @@ void ObsListDialog::setFlagUseFov(bool b)

/*
* Prepare the currently displayed/edited list for storage
* Returns QVariantList with keys={creation date, description, name, objects, sorting}
* Returns QVariantList with keys={creation date, last edit, description, name, objects, sorting}
*/
QVariantMap ObsListDialog::prepareCurrentList(QHash<QString, observingListItem> &itemHash)
{
// Creation date
const double JD = core->getJD();
const QString listCreationDate = StelUtils::julianDayToISO8601String(JD + core->getUTCOffset(JD) / 24.).replace("T", " ");
// Edit date
const double JD = StelUtils::getJDFromSystem();
const QString lastEditDate = StelUtils::julianDayToISO8601String(JD + core->getUTCOffset(JD) / 24.).replace("T", " ");
QVariantMap currentList = {
// Name, description, current date for the list, current sorting
{KEY_NAME, currentListName},
{KEY_DESCRIPTION, ui->descriptionLineEdit->text()},
{KEY_SORTING, sorting},
{KEY_CREATION_DATE, listCreationDate }};
{KEY_CREATION_DATE, ui->creationDateLineEdit->text()},
{KEY_LAST_EDIT, lastEditDate }
};

// List of objects
QVariantList listOfObjects;
Expand Down Expand Up @@ -1465,6 +1488,7 @@ const QString ObsListDialog::SHORT_NAME_VALUE = QStringLiteral("Observ
const QString ObsListDialog::KEY_DEFAULT_LIST_OLUD = QStringLiteral("defaultListOlud");
const QString ObsListDialog::KEY_OBSERVING_LISTS = QStringLiteral("observingLists");
const QString ObsListDialog::KEY_CREATION_DATE = QStringLiteral("creation date");
const QString ObsListDialog::KEY_LAST_EDIT = QStringLiteral("last edit");
const QString ObsListDialog::KEY_BOOKMARKS = QStringLiteral("bookmarks");
const QString ObsListDialog::KEY_NAME = QStringLiteral("name");
const QString ObsListDialog::KEY_NAME_I18N = QStringLiteral("nameI18n");
Expand Down
8 changes: 6 additions & 2 deletions src/gui/ObsListDialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
//! "observingLists": {
//! "{84744f7b-c353-45b0-8394-69af2a1e0917}": { // List OLUD. This is a unique ID
//! "creation date": "2022-09-29 20:05:07",
//! "last edit": "2022-11-29 22:15:38",
//! "description": "Bookmarks of previous Stellarium version.",
//! "name": "bookmarks list",
//! "objects": [ // List is stored alphabetized, but given here in contextualized order for clarity.
Expand All @@ -75,6 +76,7 @@
//! }, // end of list 84744f7b-...
//! "{bd40274c-a321-40c1-a6f3-bc8f11026326}": { // List OLUD of next list.
//! "creation date": "2022-12-21 11:12:39",
//! "last edit": "2023-07-29 22:23:38",
//! "description": "test of unification",
//! "name": "mine_edited",
//! "objects": [
Expand Down Expand Up @@ -113,7 +115,7 @@
//! Fix a confusion introduced in the 1.* series:
//! The ObsList has entries
//! - "designation": The catalog number (DSO), HIP number (star), or canonical name (planet).
//! - "nameI18n": translated name for display. Actually this is bad in case of list exchange.
//! - "nameI18n": translated name for display. Actually this is bad design in case of list exchange.
//! - "type": As given by ObjectP->getType() or getObjectType()? This was inconsistent.
//! FIXES:
//! - "designation" used in combination with type as real unique object ID. For DSO, getDSODesignationWIC() must be used.
Expand Down Expand Up @@ -292,7 +294,8 @@ class ObsListDialog : public StelDialog
QVariantMap prepareCurrentList(QHash<QString, observingListItem> &itemHash);

//! Put the bookmarks in bookmarksHash into observingLists under the listname "bookmarks list". Does not write JSON!
void saveBookmarksHashInObservingLists(const QHash<QString, observingListItem> &bookmarksHash);
//! @return OLUD of the imported bookmarks list.
QString saveBookmarksHashInObservingLists(const QHash<QString, observingListItem> &bookmarksHash);

//! Sort the obsListTreeView by the column name given in parameter
void sortObsListTreeViewByColumnName(const QString &columnName);
Expand Down Expand Up @@ -380,6 +383,7 @@ private slots:
static const QString KEY_DEFAULT_LIST_OLUD;
static const QString KEY_OBSERVING_LISTS;
static const QString KEY_CREATION_DATE;
static const QString KEY_LAST_EDIT;
static const QString KEY_BOOKMARKS;
static const QString KEY_NAME;
static const QString KEY_NAME_I18N;
Expand Down
10 changes: 10 additions & 0 deletions src/gui/obsListDialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,16 @@
<item>
<widget class="QLineEdit" name="creationDateLineEdit"/>
</item>
<item>
<widget class="QLabel" name="lastEditLabel">
<property name="text">
<string>Last edit:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lastEditLineEdit"/>
</item>
</layout>
</item>
<item row="3" column="0">
Expand Down

0 comments on commit ff1df3b

Please sign in to comment.