Skip to content

Commit

Permalink
Add tab + storage + events for starred taxa; issue #17
Browse files Browse the repository at this point in the history
  • Loading branch information
JWCook committed May 30, 2020
1 parent d4829a2 commit a6d1476
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 67 deletions.
1 change: 1 addition & 0 deletions kv/metadata.kv
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# ScrollViewY:
MDTabs:
id: metadata_tabs
background_color: app.theme_cls.primary_color

Tab:
text: 'Combined'
Expand Down
31 changes: 23 additions & 8 deletions kv/taxon_search.kv
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
size_hint_x: .4
size_hint_min_x: dp(500)
size_hint_max_x: dp(700)
# md_bg_color: app.theme_cls.bg_normal
background_color: app.theme_cls.primary_color
TaxonSearch:
TaxonSearchTab:
id: search_tab
TaxonHistoryTab:
id: history_tab
TaxonFrequencyTab:
FrequentTaxaTab:
id: frequent_tab
StarredTaxaTab:
id: starred_tab

# Info display for selected taxon
MDBoxLayout:
Expand Down Expand Up @@ -50,7 +51,7 @@
id: taxonomy_section

# Taxon search tab
<TaxonSearch@Tab>:
<TaxonSearchTab@Tab>:
text: 'magnify'
# tooltip_text: 'Search'
orientation: 'vertical'
Expand Down Expand Up @@ -88,7 +89,7 @@
id: taxon_history_list

# Frequently viewed taxa tab
<TaxonFrequencyTab@Tab>:
<FrequentTaxaTab@Tab>:
id: frequent_tab
text: 'poll'
# tooltip_text: 'Frequent'
Expand All @@ -99,7 +100,21 @@
ScrollViewY:
size_hint_max_x: dp(500)
MDList:
id: taxon_frequency_list
id: frequent_taxa_list

# Starred taxa tab
<StarredTaxaTab@Tab>:
id: starred_tab
text: 'star'
# tooltip_text: 'Starred'
orientation: 'vertical'
OneLineListItem:
font_style: 'H5'
text: 'Starred'
ScrollViewY:
size_hint_max_x: dp(500)
MDList:
id: starred_taxa_list

# Autocomplete search layout with input + dropdown
<TaxonAutocompleteSearch>:
Expand Down Expand Up @@ -134,13 +149,13 @@
id: taxon_parent_button
icon: 'arrow-up-circle-outline'
text: 'View parent'
width: dp(250)
width: dp(225)
disabled: True
TooltipIconButton:
id: selected_taxon_link_button
icon: 'link-variant'
text: 'View on iNaturalist'
width: dp(250)
width: dp(225)
disabled: True

# Taxon ancestors & children
Expand Down
1 change: 1 addition & 0 deletions naturtag/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
DEFAULT_CONFIG_PATH = join(PKG_DIR, 'default_settings.yml')
TAXON_HISTORY_PATH = join(DATA_DIR, 'taxon_history')
TAXON_FREQUENCY_PATH = join(DATA_DIR, 'taxon_frequency.json')
STARRED_TAXA_PATH = join(DATA_DIR, 'starred_taxa')

# URLs
TAXON_BASE_URL = 'https://www.inaturalist.org/taxa'
Expand Down
68 changes: 40 additions & 28 deletions naturtag/settings.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
""" Basic utilities for reading and writing settings from config files """
from collections import Counter
from collections import Counter, OrderedDict
from logging import getLogger
from os import makedirs
from os.path import isfile
from shutil import copyfile
from typing import Tuple, List, Dict, Any

import json
import yaml
Expand All @@ -14,25 +15,26 @@
DEFAULT_CONFIG_PATH,
TAXON_HISTORY_PATH,
TAXON_FREQUENCY_PATH,
STARRED_TAXA_PATH,
)

logger = getLogger().getChild(__name__)


def read_settings():
def read_settings() -> Dict[str, Any]:
""" Read settings from the settings file
Returns:
dict: Stored config state
Stored config state
"""
if not isfile(CONFIG_PATH):
reset_defaults()
logger.info(f'Reading settings to {CONFIG_PATH}')
logger.info(f'Reading settings from {CONFIG_PATH}')
with open(CONFIG_PATH) as f:
return yaml.safe_load(f)


def write_settings(new_config):
def write_settings(new_config: Dict[str, Any]):
""" Write updated settings to the settings file
Args:
Expand All @@ -58,48 +60,58 @@ def reset_defaults():

# TODO: Is there a better file format for taxon history than just a plain text file? JSON list? sqlite?
# TODO: Separately store loaded history, new history for session; only write (append) new history
def read_taxon_history():
""" Read taxon view history and frequency
def read_stored_taxa() -> Tuple[List[int], List[int], Dict[int, int]]:
""" Read taxon view history, starred, and frequency
Returns:
``list, dict``: Stored taxon history as a sequence of ints, and taxon frequecy dict
Stored taxon view history, starred, and frequency
"""
# Load history, and skip any invalid (non-int) values
if not isfile(TAXON_HISTORY_PATH):
history = []
else:
with open(TAXON_HISTORY_PATH) as f:
lines = (line.strip() for line in f.readlines())
history = [int(line) for line in lines if line and _is_int(line)]

# Load frequency, and skip any invalid (non-int) keys and values
if not isfile(TAXON_FREQUENCY_PATH):
frequency = {}
else:
with open(TAXON_FREQUENCY_PATH) as f:
frequency = json.load(f)
frequency = {int(k): int(v) for k, v in frequency.items() if _is_int(k) and _is_int(v)}
return (
read_int_list_file(TAXON_HISTORY_PATH),
read_int_list_file(STARRED_TAXA_PATH),
read_int_dict_file(TAXON_FREQUENCY_PATH),
)

return history, frequency


def write_taxon_history(history):
def write_stored_taxa(history: List[int], starred: List[int]):
""" Write taxon view history to file, along with stats on most frequently viewed taxa
Args:
history: Complete taxon history (including previously stored history)
Complete taxon history (including previously stored history)
"""
logger.info(f'Writing taxon view history ({len(history)} items)')
with open(TAXON_HISTORY_PATH, 'w') as f:
f.write('\n'.join(map(str, history)))

from collections import OrderedDict
logger.info(f'Writing starred taxa ({len(starred)} items)')
with open(STARRED_TAXA_PATH, 'w') as f:
f.write('\n'.join(map(str, set(starred))))

logger.info('Writing taxon view frequency')
with open(TAXON_FREQUENCY_PATH, 'w') as f:
counter = OrderedDict(Counter(history).most_common())
json.dump(counter, f, indent=4)


def read_int_list_file(path) -> List[int]:
""" Load a plaintext file containing a list of ints, and skip any invalid (non-int) values """
if not isfile(STARRED_TAXA_PATH):
return []
with open(path) as f:
lines = (line.strip() for line in f.readlines())
return [int(line) for line in lines if line and _is_int(line)]


def read_int_dict_file(path) -> Dict[int, int]:
""" Load a JSON file containing a mapping of int keys and values """
if not isfile(path):
return {}
else:
with open(path) as f:
int_dict = json.load(f)
return {int(k): int(v) for k, v in int_dict.items() if _is_int(k) and _is_int(v)}


def _is_int(value):
try:
int(value)
Expand Down
6 changes: 3 additions & 3 deletions naturtag/ui/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from io import BytesIO
from logging import getLogger

from kivy.properties import ObjectProperty, NumericProperty
from kivy.properties import ObjectProperty
from kivy.uix.image import AsyncImage
from kivymd.uix.imagelist import SmartTile, SmartTileWithLabel
from kivymd.uix.list import ThreeLineAvatarListItem, ILeftBody
from kivymd.uix.list import ThreeLineAvatarIconListItem, ILeftBody

from naturtag.models import get_icon_path
from naturtag.thumbnails import get_thumbnail_if_exists, get_format
Expand Down Expand Up @@ -63,7 +63,7 @@ def __init__(self, taxon_id, **kwargs):
super().__init__(source=get_icon_path(taxon_id), **kwargs)


class TaxonListItem(ThreeLineAvatarListItem):
class TaxonListItem(ThreeLineAvatarIconListItem):
""" Class that displays condensed taxon info as a list item """
def __init__(self, taxon, button_callback=None, **kwargs):
super().__init__(
Expand Down
8 changes: 4 additions & 4 deletions naturtag/ui/settings_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from naturtag.settings import (
read_settings,
write_settings,
read_taxon_history,
write_taxon_history,
read_stored_taxa,
write_stored_taxa,
reset_defaults,
)

Expand All @@ -20,7 +20,7 @@ class SettingsController:
def __init__(self, settings_screen):
self.screen = settings_screen
self.settings_dict = read_settings()
self.taxon_history, self.taxon_frequency = read_taxon_history()
self.taxon_history, self.starred_taxa, self.frequent_taxa = read_stored_taxa()

# Set default locale if it's unset
if self.inaturalist['locale'] is None:
Expand Down Expand Up @@ -54,7 +54,7 @@ def save_settings(self):
section[setting_name] = value

write_settings(self.settings_dict)
write_taxon_history(self.taxon_history)
write_stored_taxa(self.taxon_history, self.starred_taxa)

def get_control_value(self, setting_name):
""" Get the value of the control widget corresponding to a setting """
Expand Down

0 comments on commit a6d1476

Please sign in to comment.