python – Time Tracker using JSON

This is a simple command line interface program that only tracks the duration of various tasks.

Any area of ‚Äč‚Äčimprovement is welcome to be noted.

import json
import datetime

dt = datetime.datetime
td = datetime.timedelta


def encode_time(t):
    if isinstance(t, dt):
        return {
            '_type': 'datetime',
            'year': t.year,
            'month': t.month,
            'day': t.day,
            'hour': t.hour,
            'minute': t.minute,
            'second': t.second
        }
    elif isinstance(t, td):
        return {
            '_type': 'timedelta',
            'days': t.days,
            'seconds': t.seconds,
            'microseconds': t.microseconds
        }
    return t


def decode_time(t):
    if '_type' in t:

        data_type = t('_type')
        del t('_type')

        if data_type == 'datetime':
            return dt(**t)
        elif data_type == 'timedelta':
            return td(**t)
    return t


def display_pending():
    """
    pretty prints the pending dictionary.

    :return: None
    """
    print('n')
    if pending:
        max_len = max((len(key) for key in pending.keys()))
        print('Pending Activities:')
        print('-'*40)
    else:
        max_len = 0
        print('No pending data.')

    counter = 0
    for key, value in pending.items():
        duration_so_far = (dt.now() - value)

        seconds = duration_so_far.seconds
        days = duration_so_far.days
        hours = seconds // 3600
        minutes = (seconds // 60) % 60

        s = 's' if days > 1 else ''
        days = f'{days} Day{s} ' if days > 0 else ''

        s = 's' if hours > 1 else ''
        hours = f'{hours} Hour{s} ' if hours > 0 else ''

        s = 's' if minutes > 1 else ''
        minutes = f'{minutes} Min{s} ' if minutes > 0 else ''

        seconds = f'{seconds} Seconds' if seconds < 60 else ''

        print(f'({counter}) {key.capitalize():{max_len}} | {value}')  # Key and start time.

        max_len += 4  # Adding 4 to max_len to make sure this line is aligned with the one above it.
        print(f'{"." * max_len:{max_len}} | {days}{hours}{minutes}{seconds}n')  # Duration so far.
        print('-' * 40)
        max_len -= 4  # Avoiding mutating max_len.
        counter += 1


def display_durations():
    """
    Pretty prints the durations dictionary.

    :return: None
    """
    print('n')
    counter = 0

    if durations:
        max_len = max((len(key) for key in durations.keys()))
        print('Durations: ')
        print('_' * 40)
    else:
        max_len = 0
        print('No durations data')

    for key, value in durations.items():
        print(f'({counter}) {key.capitalize():{max_len}}  | {value}')
        print('-' * 40)
        counter += 1


pending = {}
durations = {}

# Reading data from pending.json
try:
    with open('pending.json', 'r') as pending_json:
        pending = json.load(pending_json, object_hook=decode_time)
        s = 's' if len(pending) > 1 else ''
        print(f'{len(pending)} pending item{s} loaded from disk')
except FileNotFoundError:
    print('  "pending.json" was not found, creating.')
    open('pending.json', 'x').close()
except json.decoder.JSONDecodeError:
    print('pending.json is empty...')

# Reading data from durations.json
try:
    with open('durations.json', 'r') as durations_json:
        durations = json.load(durations_json, object_hook=decode_time)
        s = 's' if len(durations) > 1 else ''
        print(f'{len(durations)} duration item{s} loaded from disk')
except FileNotFoundError:
    print('  "durations.json" was not found, creating.')
    open('durations.json', 'x').close()
except json.decoder.JSONDecodeError:
    print('durations.json is empty...')

if pending:
    display_pending()
if durations:
    display_durations()

# Acquiring user input.
while True:

    activity = input('n>>> ').lower()

    now = dt.now()
    start_time = pending.get(activity, None)

    if activity == 'quit':
        print('Exiting.')

        break
    elif activity == '':
        continue

    if activity in pending:
        duration = now - start_time
        print(f'"{activity}" ended. Duration: {duration}')
        durations(activity) = durations.get(activity, td(0)) + duration  # Record duration of activity.
        del pending(activity)  # Delete activity from pending list to confirm that it's completed.

        continue

    elif activity == 'man':
        activity = input('Activity Name: ')
        activity_duration = {}

        # Get num of days, hours, etc.. of manually eneterd activity.
        for parameter in ('Days', 'Hours', 'Minutes', 'Seconds', 'Microseconds'):
            while True:
                i = input(f'{parameter}: ')
                if i.isnumeric():
                    activity_duration(parameter) = int(i)
                    break
                elif i == '':
                    activity_duration(parameter) = 0
                    break

        add_minus = input('Add / Minus: ').lower()

        if add_minus == 'add':
            durations(activity) = durations.get(activity, td(0)) + td(**activity_duration)
        elif add_minus == 'minus':
            durations(activity) = durations.get(activity, td(0)) - td(**activity_duration)

        display_durations()

        continue

    elif activity == 'del':

        activity = input('Delete: ')

        if activity == 'all':
            confirmed = input('Delete Everything? y/n ')
            if confirmed == 'y':
                pending.clear()
                durations.clear()
                print('Data Cleared.')
                continue

        key_index = ((None, None))  # A list of index, key pairs for each key in pending or durations dictionaries.

        # If the activity the user wants to delete is a number, treat it as an index,
        # Unless the activity is an entry in either pending/durations lists:
        if activity.isnumeric() and activity not in set(list(pending.keys()) + list(durations.keys())):

            is_numeric = True
            activity = int(activity)
            wanted_list = input('Delete From Pending/Durations? p/d: ')

            if wanted_list == 'd':
                key_index = ((index, key) for index, key in enumerate(durations.keys()))
            elif wanted_list == 'p':
                key_index = ((index, key) for index, key in enumerate(pending.keys()))
            # If no list specified then delete from durations or pending according to index given.
            else:
                if activity <= len(durations) - 1:
                    key_index = ((index, key) for index, key in enumerate(durations.keys()))
                elif activity <= len(pending) - 1:
                    key_index = ((index, key) for index, key in enumerate(pending.keys()))

            for index, key in key_index:
                if index == activity:
                    break

            activity = key
        else:
            is_numeric = False

        if activity in pending:
            not_in_pending = False
            del pending(activity)
            print(f'"{activity.capitalize()}" deleted from pending')
        else:
            not_in_pending = True

        if activity in durations:
            not_in_durations = False
            del durations(activity)
            print(f'{activity.capitalize()} deleted from durations')
        else:
            not_in_durations = True

        if not_in_pending and not_in_durations:

            if is_numeric:
                print('No data')
            else:
                print('Key Not Found')

        continue

    elif activity == 'data':
        display_pending()
        display_durations()

        continue

    elif activity == 'help':
        print('''
Enter activity name for it to be started.
Enter the same name again for it to be ended and record it's duration.

    Commands:
        man: manually edit a duration
        del: delete an entry from pending activities and/or duration
        data: show currently pending activities and duration records

        quit: exit program and save edits.  
        ''')

        continue

    pending(activity) = now

    print(f'"{activity.capitalize()}" started on: {now}')

print('  Writing updated data to disk.')

if len(pending) > 0:
    with open('pending.json', 'w') as pending_json:
        json.dump(pending, pending_json, default=encode_time, indent=2)
else:
    # So that json doesn't dump an empty dictionary symbol: {} to the file.
    open('pending.json', 'w').close()

if len(durations) > 0:
    with open('durations.json', 'w') as durations_json:
        json.dump(durations, durations_json, default=encode_time, indent=2)
else:
    open('durations.json', 'w').close()

print('  Data updated.')

exit_confirm = input('Press Enter To Exit...')

```

Pivotal Tracker integration with Slack: How can I make changes to a tracker's history become a Slack channel?

Official integration does not offer such a feature. I tried the same with Zapier, but the triggering events we would need are not available.

Any other way to accomplish this?

tracker: do extensions like ublock really make browsing more private?

There are several browser extensions that block the crawler that is collecting metadata. But different blockers are blocking different cookies – their interpretation of what the tracker is is different. One will say, "He is a tracker." The second: "It's not a tracker, accept it!" The third will say: "Well, it is a tracker, but I must accept it due to the proper functioning of the website."

So my conclusion may be that extensions to block cookies will not disable tracking, they will only limit it. Therefore, your metadata from visiting some pages will still leak.

Text Based Card Tracker – Game Development Stack Swap

I want to build a card tracker that reads the game text and keeps track of the cards that are out.

Something like Overwolf does, but for a card game. Is there a simple way to do it?

I mailed and asked Overwolf, who suggested using image recognition. That sounds complex (I'm lucky with any experience on it).

The tracker I want to build is for Twilight fight and I would like to automate this card counter so that it tracks the cards that have been played from a text box in the game that indicates their name.

enter the image description here

It is my first question here, I hope it is not too strange.

Google Ad Tracker (other than GA)?

Any reason to use a tracker other than Google Analytics for Google Ads?

Torrent or tracker from Germany

Hello, Where can I find Germany Torrent or Tracker to download Germany Movies or TV?

web scraping – web tracker in Rust

There is a very common tool accessible through cargo You can format all the code in your project according to the official Rust style guide. Many of Rust's main open source libraries use this tool (and even apply it through CI in pull requests), which you can access through cargo fmt. You can also customize your output using a .rustfmt configuration file See the project repository under the official rust-lang organization here.

The closure that begins here contains several .unwrap() because some locks you make may not produce anything.

        new_urls.par_iter().for_each(|url| {
            // ...
            found_urls.lock().unwrap().extend(&links);
            visited.lock().unwrap().insert(url.to_string());

Instead of .unwrap()If something goes wrong, which could poison all your blocked Mutex, consider try_for_each.

With try_for_each, your closing has to return a Result or a Option instead of () (nothing, unity). This allows you to use the Rust special ? operator, which is like a shorter version of .unwrap() That is actually a little better because instead of blocking your program, it returns the error to be handled elsewhere.

            found_urls.lock().ok()?.extend(links);
            visited.lock().ok()?.insert(url.to_string());
            Some(())

Note that in this case, we have to use .ok() because the PoisonError Mutex's return also contains a reference to Mutex, which is not thread safe. A best practice here could be to use a custom error enumeration. (more on that later)

This practice can be propagated through the code base.

The Rust compiler is your friend! When I compile your code on my machine, I receive several warnings.

warning: unused variable: `dir`
  --> srcmain.rs:60:9
   |
60 |     let dir = fs::create_dir_all(format!("static{}", path)).unwrap();
   |         ^^^ help: consider prefixing with an underscore: `_dir`
   |
   = note: `#(warn(unused_variables))` on by default

warning: variable does not need to be mutable
  --> srcmain.rs:73:9
   |
73 |     let mut visited = Arc::new(Mutex::new(HashSet::new()));
   |         ----^^^^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: `#(warn(unused_mut))` on by default

warning: variable does not need to be mutable
  --> srcmain.rs:82:13
   |
82 |         let mut found_urls = Arc::new(Mutex::new(HashSet::new()));
   |             ----^^^^^^^^^^
   |             |
   |             help: remove this `mut`

warning: unused `std::result::Result` that must be used
  --> srcmain.rs:61:5
   |
61 |     fs::write(format!("static{}/index.html", path), content);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#(warn(unused_must_use))` on by default
   = note: this `Result` may be an `Err` variant, which should be handled

All of these can be handled directly.

The mobile usability report only shows 1 valid page since Google changed the tracker to smartphone

My website has all its indexed content, in fact, the coverage shows 45 valid indexed pages:

enter the description of the image here

At some point, Google decided to change from desktop to mobile indexer and suddenly the usability report showed a drop in the number of valid pages there:

enter the description of the image here

This is strange because I am not seeing any mistakes. The site has no manual actions and all mobile usability tests say everything is fine. Does anyone have any idea what should be happening?

Astrophotography – Barn Door Tracker Question

I'm thinking of building a Barn door tracker for some basic Astrophotography. I've seen some of the compilations of people like this:

https://www.nutsvolts.com/magazine/article/january2015_Wierenga

In all the versions I've seen, the camera is mounted a few inches from the axis of rotation. If I have to guess, the center of the camera lens is probably 5-6 inches from the axis. Will this not cause error?

If I point the camera at the polar star, I suspect that this shift will cause the camera lens to prescribe a small circle instead of the lens.

Is this understanding correct? No one has mentioned this anywhere.

java – Should a Move and a Tracker be separate classes to maintain SRP?

I am practicing a Java code to better understand the principle of sole responsibility. Currently, I am working with a network of data structures, where each structure implements an interface called Location.

public interface Location {

    void removeObject(Object o);
    void addObject(Object o);
}

Different locations store and retrieve objects differently, so everyone needs their own method to add / delete them. The goal is to be able to freely move objects between locations and find where the objects are currently. I have two classes called to move the Objects, one to make the actual movement of the Objects (the Move) and another to keep track of where the Objects are so that they can be retrieved for the Move class (the Tracker).

public class Database {

private Mover mover;
private Tracker tracker;

//Contains methods for moving specific Objects to specific Locations.

}

public class Mover {

void moveObject(Object o, Location a, Location b)
{
  a.removeObject(o);
  b.addObject(o);
}

}

public class Tracker {

Map> trackerMap;

void updateTracker(Object o, Location a, Location b)
{
    trackerMap.remove(a);
    trackerMap.put(b, o);
}

Object lookupObject(Location l, Object o)
{
    List objects = trackerMap.get(l);
    return objects.get(o);
}

}

However, I have some concerns about this design. Because Move and Tracker have been divided into separate classes, data structures are essentially stored twice. Once for the Tracker map and once for the actual data structures, the Move is modifying. It seems prone to errors. If the two were out of sync for any reason, the Move could end up trying to move things that do not exist, and the Tracker may be unable to find them. I still have the feeling that the Move should be directly modifying the Tracker map when moving Objects, to ensure they remain synchronized. However, if the Move begins to modify the Tracker, then the Tracker does not need to be its own class. And combining the two, from what I understand, would violate the SRP.

How can I improve the design of this database while maintaining the SRP?