commit
c4d10c7e4f
10 changed files with 250 additions and 17 deletions
42
README.md
42
README.md
|
@ -68,6 +68,48 @@ install-directory is in your `$PATH`), or install the `imag` binary to call `ima
|
||||||
<modulename>` (also if everything is in your `$PATH`).
|
<modulename>` (also if everything is in your `$PATH`).
|
||||||
|
|
||||||
|
|
||||||
|
## Example usage
|
||||||
|
|
||||||
|
As imag is a big and complex project, we cannot show all tools of the suite
|
||||||
|
here. But to give you some idea, here's an example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lets initialize imag
|
||||||
|
imag init
|
||||||
|
|
||||||
|
# Recursively import vcf files
|
||||||
|
imag contact import /home/user/contacts
|
||||||
|
|
||||||
|
# Create a contact (vcf) in the private collection
|
||||||
|
imag contact create --file /home/user/contacts/private
|
||||||
|
|
||||||
|
# Add a diary entry
|
||||||
|
imag diary -p private create
|
||||||
|
|
||||||
|
# Uh, I forgot something in a diary entry, select one and edit it
|
||||||
|
# use the `fzf` tool here (not a part of imag) to select from the IDs
|
||||||
|
imag diary -p private list | fzf -m | imag edit -I
|
||||||
|
|
||||||
|
# Link a contact to the diary entry
|
||||||
|
imag link diary/private/2018/01/01/00:00:00 contact/bc222298-casf-40a4-bda1-50aa980a68c9
|
||||||
|
|
||||||
|
# Annotate a contact with some notes
|
||||||
|
imag annotate add contact/bc222298-casf-40a4-bda1-50aa980a68c9 contact-notes
|
||||||
|
|
||||||
|
# Write down some notes named "pineapple"
|
||||||
|
imag notes create "pineapple"
|
||||||
|
|
||||||
|
# Where was that contact again?
|
||||||
|
imag grep Eva
|
||||||
|
# Okay, we need to add some imag-internal notes to that contact
|
||||||
|
imag grep Eva -l | imag edit -I
|
||||||
|
|
||||||
|
# Now save our work
|
||||||
|
imag git add . # "imag-git" simply calls git in the imag store
|
||||||
|
imag git commit -m 'Commit message'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Staying up-to-date
|
## Staying up-to-date
|
||||||
|
|
||||||
We have a [official website for imag](https://imag-pim.org), where I post
|
We have a [official website for imag](https://imag-pim.org), where I post
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "imag-mv"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
description = "Part of the imag core distribution: imag command"
|
description = "Part of the imag core distribution: imag-mv command"
|
||||||
|
|
||||||
keywords = ["imag", "PIM", "personal", "information", "management"]
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
readme = "../../../README.md"
|
readme = "../../../README.md"
|
||||||
|
|
|
@ -22,6 +22,20 @@ This section contains the changelog from the last release to the next release.
|
||||||
* Minor changes
|
* Minor changes
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
|
|
||||||
|
## 0.7.1
|
||||||
|
|
||||||
|
Bugfix release for fixing:
|
||||||
|
|
||||||
|
* `libimagdiary` did not return the youngest, but the oldest entry id on `::get_youngest_entry_id()`.
|
||||||
|
* `imag-view` should not always wrap the text, only if -w is passed
|
||||||
|
* `imag-log show` should order by date
|
||||||
|
* `imag` does not inherit stdout if detecting imag command versions.
|
||||||
|
* `imag-contact import` does only allow absolute pathes
|
||||||
|
* `imag-contact` has most fields optional now, only name is required
|
||||||
|
* `imag-contact` automatically creates UID
|
||||||
|
* `imag-contact` automatically generates/warns about missing file extension
|
||||||
|
|
||||||
|
|
||||||
## 0.7.0
|
## 0.7.0
|
||||||
|
|
||||||
* Major changes
|
* Major changes
|
||||||
|
|
23
etc/imag-contact-mutt-query-command-helper.sh
Normal file
23
etc/imag-contact-mutt-query-command-helper.sh
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# A helper script for the mutt query_command
|
||||||
|
#
|
||||||
|
# Use
|
||||||
|
#
|
||||||
|
# query_command = "/path/to/this/script %s"
|
||||||
|
#
|
||||||
|
# in mutt to search contacts from mutt with imag.
|
||||||
|
#
|
||||||
|
|
||||||
|
# mutt wants a one-line status message before the list of contacts.
|
||||||
|
echo "Searching with imag ..."
|
||||||
|
imag contact find "$1" --json | \
|
||||||
|
jq --raw-output '
|
||||||
|
map(.fullname[0] as $FN
|
||||||
|
| .email
|
||||||
|
| map({email: ., fullname: $FN}))
|
||||||
|
| flatten
|
||||||
|
| map([.email.address, .fullname, .email.properties.TYPE])
|
||||||
|
| .[]
|
||||||
|
| @tsv
|
||||||
|
'
|
152
etc/ioverlander-wiki-import.sh
Normal file
152
etc/ioverlander-wiki-import.sh
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# This script imports iOverlander data into a imag-wiki
|
||||||
|
#
|
||||||
|
# Requirements
|
||||||
|
# ============
|
||||||
|
#
|
||||||
|
# * imag-wiki
|
||||||
|
# * imag-gps
|
||||||
|
# * imag-store
|
||||||
|
# * jq
|
||||||
|
#
|
||||||
|
# Usage
|
||||||
|
# =====
|
||||||
|
#
|
||||||
|
# Download the JSON data files from app.ioverlander.com and run them through
|
||||||
|
# this script.
|
||||||
|
#
|
||||||
|
# ./this/script wiki-name ioverlander-file.json
|
||||||
|
#
|
||||||
|
# What it does
|
||||||
|
# ============
|
||||||
|
#
|
||||||
|
# It imports each location from the JSON data into one entry at:
|
||||||
|
#
|
||||||
|
# <wiki_name>/<country>/<location name>_<location id>
|
||||||
|
#
|
||||||
|
# (The location id is added because names are sometimes reused)
|
||||||
|
#
|
||||||
|
# * It uses imag-gps to set the GPS data
|
||||||
|
# * It sets the header of the entry to the data it finds from the JSON object.
|
||||||
|
# It puts all data in the "userdata.ioverlander" namespace.
|
||||||
|
# It strips whitespace from the keys.
|
||||||
|
# It ignores the GPS data from the JSON object though, as this was added via
|
||||||
|
# imag-gps.
|
||||||
|
# * It uses the "description" field as entry content.
|
||||||
|
#
|
||||||
|
# Warning
|
||||||
|
# =======
|
||||||
|
#
|
||||||
|
# As the ioverlander data sometimes contains "/" in the name, some entries are
|
||||||
|
# namespaced. This has to be fixed and the linkings have to be adapted. This is
|
||||||
|
# not yet handled by this script.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
WIKI_NAME="$1"
|
||||||
|
IOVERLANDER_FILE_PATH="$2"
|
||||||
|
|
||||||
|
[[ -z "$WIKI_NAME" ]] && echo "Wiki name missing" && exit 1
|
||||||
|
[[ -z "$IOVERLANDER_FILE_PATH" ]] && echo "JSON file missing" && exit 1
|
||||||
|
|
||||||
|
imag wiki create-wiki "$WIKI_NAME" --no-edit
|
||||||
|
echo "Created imag wiki '$WIKI_NAME'"
|
||||||
|
|
||||||
|
tobool() {
|
||||||
|
if [[ "$1" == "Yes" ]]; then
|
||||||
|
echo "true"
|
||||||
|
elif [[ "$1" == "No" ]]; then
|
||||||
|
echo "false"
|
||||||
|
else
|
||||||
|
echo "\"$1\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
object_to_vars() {
|
||||||
|
jq -r -c 'to_entries | .[] | .key + "=\"" + (.value | tostring | gsub("\\\""; "")) + "\""'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# The comments in this function can be outcommented for debugging
|
||||||
|
process_object() {
|
||||||
|
read -r json
|
||||||
|
|
||||||
|
local location_latitude
|
||||||
|
local location_longitude
|
||||||
|
local location_altitude
|
||||||
|
local location_horizontal_accuracy
|
||||||
|
local location_vertical_accuracy
|
||||||
|
local amenities_Open
|
||||||
|
local amenities_Electricity
|
||||||
|
local amenities_Wifi
|
||||||
|
local amenities_Kitchen
|
||||||
|
local amenities_Restaurant
|
||||||
|
local amenities_Showers
|
||||||
|
local amenities_Water
|
||||||
|
local amenities_Toilets
|
||||||
|
local amenities_Big_rig_friendly
|
||||||
|
local amenities_Tent_friendly
|
||||||
|
local amenities_Pet_friendly
|
||||||
|
local id
|
||||||
|
local name
|
||||||
|
local description
|
||||||
|
local date_verified
|
||||||
|
local category_icon_path
|
||||||
|
local category_icon_pin_path
|
||||||
|
local country
|
||||||
|
local category
|
||||||
|
|
||||||
|
#echo "----------------------------------------------------------"
|
||||||
|
#echo "JSON = $json"
|
||||||
|
#echo "-----------"
|
||||||
|
#echo $json | jq '.location' | object_to_vars | sed 's,^,location_,'
|
||||||
|
#echo $json | jq '.amenities' | object_to_vars | sed 's,^,amenities_,; s, Big,_big,; s, Rig,_rig,; s, Friendly,_friendly,'
|
||||||
|
#echo $json | jq 'del(.location)|del(.amenities)' | object_to_vars
|
||||||
|
|
||||||
|
eval $(echo $json | jq '.location' | object_to_vars | sed 's,^,location_,')
|
||||||
|
eval $(echo $json | jq '.amenities' | object_to_vars | sed 's,^,amenities_,; s, Big,_big,; s, Rig,_rig,; s, Friendly,_friendly,')
|
||||||
|
eval $(echo $json | jq 'del(.location)|del(.amenities)' | object_to_vars)
|
||||||
|
|
||||||
|
local ctry_slug=$(echo $country | sed 's, ,_,g')
|
||||||
|
local name_slug=$(echo $name | sed 's, ,_,g')
|
||||||
|
|
||||||
|
echo "create = $ctry_slug/${name_slug}_${id}"
|
||||||
|
|
||||||
|
local article=$(imag wiki --wiki "$WIKI_NAME" create "$ctry_slug/${name_slug}_${id}" --no-edit --print-id || exit 1)
|
||||||
|
|
||||||
|
echo "ARTICLE = $article"
|
||||||
|
|
||||||
|
imag gps add --lat="$location_latitude" --long="$location_longitude" "$article" || {
|
||||||
|
echo "GPS setting failed"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
imag store update --id "$article" \
|
||||||
|
--header \
|
||||||
|
"userdata.ioverlander.id=\"$id\"" \
|
||||||
|
"userdata.ioverlander.name=\"$name\"" \
|
||||||
|
"userdata.ioverlander.date_verified=\"$date_verified\"" \
|
||||||
|
"userdata.ioverlander.country=\"$country\"" \
|
||||||
|
"userdata.ioverlander.category=\"$category\"" \
|
||||||
|
"userdata.ioverlander.amenities.open=$(tobool "$amenities_Open")" \
|
||||||
|
"userdata.ioverlander.amenities.electricity=$(tobool "$amenities_Electricity")" \
|
||||||
|
"userdata.ioverlander.amenities.wifi=$(tobool "$amenities_Wifi")" \
|
||||||
|
"userdata.ioverlander.amenities.kitchen=$(tobool "$amenities_Kitchen")" \
|
||||||
|
"userdata.ioverlander.amenities.restaurant=$(tobool "$amenities_Restaurant")" \
|
||||||
|
"userdata.ioverlander.amenities.showers=$(tobool "$amenities_Showers")" \
|
||||||
|
"userdata.ioverlander.amenities.water=$(tobool "$amenities_Water")" \
|
||||||
|
"userdata.ioverlander.amenities.toilets=$(tobool "$amenities_Toilets")" \
|
||||||
|
"userdata.ioverlander.amenities.big_rig_friendly=$(tobool "$amenities_Big_rig_friendly")" \
|
||||||
|
"userdata.ioverlander.amenities.tent_friendly=$(tobool "$amenities_Tent_friendly")" \
|
||||||
|
"userdata.ioverlander.amenities.pet_friendly=$(tobool "$amenities_Pet_friendly")" \
|
||||||
|
--content \""$description\"" || {
|
||||||
|
echo "header/content setting failed";
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cat "$IOVERLANDER_FILE_PATH" | jq -c '.[]' | while read line; do
|
||||||
|
process_object
|
||||||
|
done
|
||||||
|
|
|
@ -96,6 +96,7 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
|
||||||
};
|
};
|
||||||
*self = FSFileAbstractionInstance::File(file, path);
|
*self = FSFileAbstractionInstance::File(file, path);
|
||||||
if let FSFileAbstractionInstance::File(ref mut f, _) = *self {
|
if let FSFileAbstractionInstance::File(ref mut f, _) = *self {
|
||||||
|
trace!("Writing buffer...");
|
||||||
return f.write_all(&buf).chain_err(|| SEK::FileNotWritten);
|
return f.write_all(&buf).chain_err(|| SEK::FileNotWritten);
|
||||||
}
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
|
@ -186,11 +187,14 @@ fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
|
||||||
|
|
||||||
fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
|
fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
|
||||||
if let Some(parent) = p.as_ref().parent() {
|
if let Some(parent) = p.as_ref().parent() {
|
||||||
debug!("Implicitely creating directory: {:?}", parent);
|
trace!("'{}' is directory = {}", parent.display(), parent.is_dir());
|
||||||
|
if !parent.is_dir() {
|
||||||
|
trace!("Implicitely creating directory: {:?}", parent);
|
||||||
if let Err(e) = create_dir_all(parent) {
|
if let Err(e) = create_dir_all(parent) {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
OpenOptions::new().write(true).read(true).create(true).open(p)
|
OpenOptions::new().write(true).read(true).create(true).open(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,7 @@ impl StoreEntry {
|
||||||
fn write_entry(&mut self, entry: &Entry) -> Result<()> {
|
fn write_entry(&mut self, entry: &Entry) -> Result<()> {
|
||||||
if self.is_borrowed() {
|
if self.is_borrowed() {
|
||||||
assert_eq!(self.id, entry.location);
|
assert_eq!(self.id, entry.location);
|
||||||
|
trace!("Writing entry...");
|
||||||
self.file
|
self.file
|
||||||
.write_file_content(entry)
|
.write_file_content(entry)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
|
@ -442,11 +443,13 @@ impl Store {
|
||||||
|
|
||||||
debug!("Writing Entry");
|
debug!("Writing Entry");
|
||||||
se.write_entry(&entry.entry)?;
|
se.write_entry(&entry.entry)?;
|
||||||
|
trace!("Entry written");
|
||||||
if modify_presence {
|
if modify_presence {
|
||||||
debug!("Modifying presence of {} -> Present", entry.get_location());
|
debug!("Modifying presence of {} -> Present", entry.get_location());
|
||||||
se.status = StoreEntryStatus::Present;
|
se.status = StoreEntryStatus::Present;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!("Entry updated successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,6 +801,7 @@ impl<'a> Drop for FileLockEntry<'a> {
|
||||||
use libimagerror::trace::trace_error_dbg;
|
use libimagerror::trace::trace_error_dbg;
|
||||||
trace!("Dropping: {:?} - from FileLockEntry::drop()", self.get_location());
|
trace!("Dropping: {:?} - from FileLockEntry::drop()", self.get_location());
|
||||||
if let Err(e) = self.store._update(self, true) {
|
if let Err(e) = self.store._update(self, true) {
|
||||||
|
trace!("Error happened in FileLockEntry::drop() while Store::update()ing");
|
||||||
trace_error_dbg(&e);
|
trace_error_dbg(&e);
|
||||||
if_cfg_panic!("ERROR WHILE DROPPING: {:?}", e);
|
if_cfg_panic!("ERROR WHILE DROPPING: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,6 +388,8 @@ pub mod iter {
|
||||||
impl InternalLinker for Entry {
|
impl InternalLinker for Entry {
|
||||||
|
|
||||||
fn get_internal_links(&self) -> Result<LinkIter> {
|
fn get_internal_links(&self) -> Result<LinkIter> {
|
||||||
|
debug!("Getting internal links");
|
||||||
|
trace!("Getting internal links from header of '{}' = {:?}", self.get_location(), self.get_header());
|
||||||
let res = self
|
let res = self
|
||||||
.get_header()
|
.get_header()
|
||||||
.read("links.internal")
|
.read("links.internal")
|
||||||
|
@ -400,6 +402,8 @@ impl InternalLinker for Entry {
|
||||||
fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<LinkIter> {
|
fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<LinkIter> {
|
||||||
use internal::iter::IntoValues;
|
use internal::iter::IntoValues;
|
||||||
|
|
||||||
|
debug!("Setting internal links");
|
||||||
|
|
||||||
let self_location = self.get_location().clone();
|
let self_location = self.get_location().clone();
|
||||||
let mut new_links = vec![];
|
let mut new_links = vec![];
|
||||||
|
|
||||||
|
@ -430,11 +434,13 @@ impl InternalLinker for Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_internal_link(&mut self, link: &mut Entry) -> Result<()> {
|
fn add_internal_link(&mut self, link: &mut Entry) -> Result<()> {
|
||||||
|
debug!("Adding internal link: {:?}", link);
|
||||||
let location = link.get_location().clone().into();
|
let location = link.get_location().clone().into();
|
||||||
add_internal_link_with_instance(self, link, location)
|
add_internal_link_with_instance(self, link, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()> {
|
fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()> {
|
||||||
|
debug!("Removing internal link: {:?}", link);
|
||||||
let own_loc = self.get_location().clone().without_base();
|
let own_loc = self.get_location().clone().without_base();
|
||||||
let other_loc = link.get_location().clone().without_base();
|
let other_loc = link.get_location().clone().without_base();
|
||||||
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if [[ -z "$1" ]]; then
|
|
||||||
echo "No rev list given"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
for rev in $(git rev-list $1); do
|
|
||||||
if git notes --ref=changelog list $rev &> /dev/null; then
|
|
||||||
git log -n 1 --show-notes=* --pretty="format:* %N" $rev
|
|
||||||
fi
|
|
||||||
done
|
|
|
@ -10,7 +10,7 @@ fi
|
||||||
for rev in $(git rev-list "$since"..HEAD | tac); do
|
for rev in $(git rev-list "$since"..HEAD | tac); do
|
||||||
if git notes --ref=changelog list $rev &> /dev/null; then
|
if git notes --ref=changelog list $rev &> /dev/null; then
|
||||||
output=$(git notes --ref=changelog show $rev | sed '2,$s/^/ /')
|
output=$(git notes --ref=changelog show $rev | sed '2,$s/^/ /')
|
||||||
echo "* $output"
|
echo "* [$(echo ${rev:0:10})] $output"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue