text:
# MySQL

* [MySQL](https://www.mysql.com/).
* [MySQL Modeling](http://www.mysql.com/products/workbench/).

## Table of contents
- [Types](#types)
- [Create](#create)
- [Insert](#insert)
- [Delete](#delete)
- [Update](#update)
- [Select](#select)
- [Enum values](#enum-values)
- [Alter](#alter)
- [Indexes](#indexes)
- [Groups](#groups)
- [Joins](#joins)
- [Relations](#relations)
- [Cascade](#cascade)

## Types

```sql
# Numeric Data Types:
TINYINT         # 4 digits
SMALLINT        # 5 digits
MEDIUMINT       # 9 digits
INT             # 11 digits
BIGINT          # 20 digits
FLOAT(M,D)      # 10, 24 digits
DOUBLE(M,D)     # 16, 53 digits
DECIMAL(M,D)

# Date and Time Types:
DATE            # YYYY-MM-DD
DATETIME        # YYYY-MM-DD HH:MM:SS
TIMESTAMP       # YYYYMMDDHHMMSS
TIME            # HH:MM:SS
YEAR(M).        # year in 2-digit or 4-digit YEAR(2) YEAR(4)

# String Types:
CHAR(M)                     # fixed length 1-255 character
VARCHAR(M)                  # variable length 1-255 character
BLOB or TEXT                # variable length 1-65535 character
TINYBLOB or TINYTEXT        # variable length 1-255 character
MEDIUMBLOB or MEDIUMTEXT    # variable length 1-16777215 character
LONGBLOB or LONGTEXT        # variable length 1-4294967295 character
ENUM('A', 'B', 'C')         # values declared(1,2,..) or NULL
```

## Create
```sql
CREATE TABLE Users(
    name VARCHAR(128)
    email VARCHAR(128)
)
```

## Insert
```sql
INSERT INTO Users(name, email, ..., n) VALUES ('Nombraco'<EMAIL_ADDRESS>..., n)
```

## Delete
```sql
DELETE FROM Users WHERE<EMAIL_ADDRESS>```

## Update
```sql
UPDATE Users SET name='Charles' WHERE<EMAIL_ADDRESS>```

## Select
```sql
SELECT * FROM Users
SELECT * FROM Users WHERE<EMAIL_ADDRESS>```

### Constraints

TODO

#### Primary Key
```sql
CREATE TABLE Orders(
    O_Id int NOT NULL,
    P_Id int,
    PRIMARY KEY (O_Id),
    FOREIGN KEY (P_Id) REFERENCES Persons(P_Id)
)
```

#### Not null
```sql
CREATE TABLE Orders(
    O_Id int NOT NULL,
)
```

## Enum values
```sql
CREATE TABLE shirts (
    name VARCHAR(40),
    size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);
```

## Alter
```sql
ALTER TABLE table_name
ADD column_name datatype

# Add attribute DateOfBirth to Users
ALTER TABLE Users
ADD DateOfBirth date
```

## Indexes
```sql
CREATE INDEX index_name
ON table_name (column_name)

CREATE INDEX UserLastNameIndex
ON Users (LastName)
```

## Groups
```sql
SELECT * FROM Users ORDER BY email
SELECT * FROM Users ORDER BY name
SELECT * FROM Users ORDER BY email DESC LIMIT 10
SELECT COUNT(*) FROM Users # Count
```

## Joins
From http://www.w3schools.com/sql/sql_join.asp
* INNER JOIN
* LEFT JOIN
* RIGHT JOIN
* FULL JOIN

```sql
-- INNER JOIN (INNER JOIN is the same as JOIN)
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;

-- LEFT JOIN (some databases call as LEFT OUTER JOIN)
SELECT column_name(s)
FROM table1
LEFT JOIN table2
ON table1.column_name=table2.column_name;

-- RIGHT JOIN (some databases call as RIGHT OUTER JOIN)
SELECT column_name(s)
FROM table1
RIGHT JOIN table2
ON table1.column_name=table2.column_name;

-- FULL JOIN
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name=table2.column_name;
```

## Relations
### One-to-many relation
Put foerign key in the many side.

### Many-to-many relation
Se componen por 2 claves primarias ajenas. Las dos claves juntas se comportan como primaria compuesta.

```sql
CREATE TABLE Member (
    user_id     INTEGER,
    course_id   INTEGER,
    role        INTEGER,
    PRIMARY KEY(user_id, course_id)
)
```

## Cascade
```sql
CREATE TABLE inventory(
    inventory_id INT PRIMARY KEY,
    product_id INT NOT NULL,
    quantity INT,
    min_level INT,
    max_level INT,
    CONSTRAINT fk_inv_product_id
        FOREIGN KEY (product_id)
        REFERENCES products (product_id)
        ON DELETE CASCADE
);
```

## Scripts

```sql
-- Given the following table 'decimals':
-- ** decimals table schema **
--     id
--     number1
--     number2
-- Return a table with two columns (abs, log) where the values in abs are the absolute values of number1 and the values in log are values from number2 in logarithm to base 64.
SELECT
  ABS(d.number1) as abs,
  LOG(64, d.number2) as log
FROM decimals as d
```

```sql
-- Given the following table 'decimals':
-- ** decimals table schema **
--     id
--     number1
--     number2
-- Return a table with one column (towardzero) where the values are the result of number1 + number2 truncated towards zero.
SELECT
 TRUNC(d.number1 + d.number2) as towardzero
FROM decimals as d
```

```sql
-- The query should output the following columns:
--     customer_id [int4]
--     email [varchar]
--     payments_count [int]
--     total_amount [float]
-- and has the following requirements:
--     only returns the 10 top customers, ordered by total amount spent from highest to lowest
SELECT
  customer.customer_id,
  customer.email,
  COUNT(payment.payment_id) AS payments_count,
  CAST(SUM(payment.amount) AS float) AS total_amount
FROM customer
JOIN payment
  ON customer.customer_id = payment.customer_id
GROUP BY customer.customer_id
ORDER BY total_amount DESC
LIMIT 10
```

```sql
-- Your are working for a company that wants to reward its top 10 customers with a free gift. You have been asked to generate a simple report that returns the top 10 customers by total amount spent ordered from highest to lowest. Total number of payments has also been requested.

-- The query should output the following columns:

--     customer_id [int4]
--     email [varchar]
--     payments_count [int]
--     total_amount [float]

-- and has the following requirements:

--     only returns the 10 top customers, ordered by total amount spent from highest to lowest
SELECT
  c.customer_id AS customer_id,
  c.email AS email,
  COUNT(p.amount) as payments_count,
  SUM(CAST(p.amount AS FLOAT)) as total_amount
FROM customer as c, payment as p
WHERE c.customer_id = p.customer_id
GROUP BY c.customer_id
ORDER BY total_amount DESC
LIMIT 10
```

```sql
-- Timmy works for a statistical analysis company and has been given a task of calculating the highest average salary for a given job, the sample is compiled of 100 applicants each with a job and a salary. Timmy must display each unique job, the total average salary, the total people and the total salary and order by highest average salary. Timmy has some bugs in his query, help Timmy fix his query so he can keep his job!
-- people table schema
--     id
--     name
-- job table schema
--     id
--     people_id
--     job_title
--     salary
-- resultant table schema
--     job_title (unique)
--     average_salary (float, 2 dp)
--     total_people (int)
--     total_salary (float, 2 dp)
SELECT
  j.job_title,
  ROUND(AVG(j.salary),2)::FLOAT as average_salary,
  COUNT(p.id) as total_people,
  ROUND(SUM(j.salary),2)::FLOAT as total_salary
  FROM people AS p
    JOIN job AS j ON p.id = j.people_id
  GROUP BY j.job_title
  ORDER BY average_salary DESC
```

```sql
-- You have access to two tables named top_half and bottom_half, as follows:
-- top_half schema
--     id
--     heads
--     arms
-- bottom_half schema
--     id
--     legs
--     tails
-- You must return a table with the format as follows:
-- output schema
--     id
--     heads
--     legs
--     arms
--     tails
--     species
-- The IDs on the tables match to make a full monster. For heads, arms, legs and tails you need to draw in the data from each table.
-- For the species, if the monster has more heads than arms, more tails than legs, or both, it is a 'BEAST' else it is a 'WEIRDO'. This needs to be captured in the species column.
SELECT
  top.id,
  top.heads,
  bottom.legs,
  top.arms,
  bottom.tails,
  CASE WHEN top.heads > top.arms OR bottom.tails > bottom.legs
    THEN 'BEAST'
    ELSE 'WEIRDO'
  END as species

FROM
  top_half AS top
JOIN
  bottom_half AS bottom
ON top.id = bottom.id
ORDER BY species
```

repository: pabagan/knowledgebase

gha_language: Shell

repo_name: pabagan/knowledgebase


text:
# Nexss Edit

Edit files with common editors

## Edit files

Every filetype can have own editor type. All editors are specified in the nexss programmer, however which one is used for particular language is defined in the language config file.

```sh
nexss edit filename.js # will install default editor if not there, and open it.
# if file is not specified will use "." as current folder.
```

license: MIT

repository: makselp/cli

branch_name: refs/heads/master

extension: md

license_type: permissive

repo_name: makselp/cli


text:
# MissinKit
[![NuGet](https://img.shields.io/nuget/v/MissinKit.svg)](https://www.nuget.org/packages/MissinKit)

## License
The code is released under the [MIT License](https://opensource.org/licenses/MIT).

## What's Inside?
#### iOS Native Pluralization
As Xamarin's current `NSString.LocalizedFormat()` method is useless,
there's no way to use iOS native pluralization. To turn this very important feature on,
there's the `NSStringUtility.LocalizedFormat()` method to replace Xamarin's counterpart:
```csharp
NSStringUtility.LocalizedFormat(NSBundle.MainBundle.GetLocalizedString("%d file(s) remaining"), 2)
```
If key is found, it formats the string accordinig to the .stringsdict file. Otherwise, it falls back and prints "2 file(s) remaining".

WARNING! The `NSStringUtility.LocalizedFormat()` method does not work on 64-bit simulators.

Instead of using the `NSStringUtility.LocalizedFormat()` or `NSBundle.GetLocalizedString()` methods directly,
use the convenient extension methods which are more than enough for the vast majority of cases:
```csharp
// instead of
str = NSBundle.MainBundle.GetLocalizedString("key");
// use
str = "key".Localize();

// instead of
str = string.format(NSBundle.MainBundle.GetLocalizedString("key"), 2, "text");
// use
str = "key".Localize(2, "text");

// instead of
str = NSStringUtility.LocalizedFormat(NSBundle.MainBundle.GetLocalizedString("key"), 3);
// use
str = "key".Localize(3);
```
These methods work with both .strings and .stringsdict files.

#### Network Reachability
The `Reachability` class is a port of Apple's Reachability sample app to monitor the network state of an iOS device.
```csharp
var r = new Reachability("apple.com");
r.ReachabilityChanged += (s, e) => Debug.WriteLine($"Network Status: {r.Status}");
```

#### Date Utilities
There are two extension methods providing a quick way to convert `DateTime` to `NSDate` and vice-versa.
```csharp
NSDate nsdate = DateTime.Now.ToNSDate();
DateTime datetime = nsdate.ToDateTime();
```
Not a big deal, but convenient.

#### Changing Current Locale on the Fly
Since iOS doesn't provide an easy way to change `NSLocale.CurrentLocale` programmatically,
there's a utility to help with that.
```csharp
var date = new DateTime(2018, 1, 1).ToNSDate();
var formatter = new NSDateFormatter() { DateFormat = "EEEE" };

L10n.OverrideCurrentLocale("es_MX");

formatter.Locale = NSLocale.CurrentLocale;
Debug.WriteLine(formatter.StringFor(date)); // outputs 'lunes'

L10n.RestoreCurrentLocale();

formatter.Locale = NSLocale.CurrentLocale;
Debug.WriteLine(formatter.StringFor(date)); // outputs 'Monday'
```
Overriding or restoring the current locale triggers `NSCurrentLocaleDidChangeNotification`.

#### iOS 11 Fallback
Since iOS 11 introduced some breaking changes to UIView layout handling,
there are a few fallback extension methods to reduce boilerplate code required to handle those changes.

The `SafeAreaLayoutGuide()` method returns the `UIVIew.SafeAreaLayoutGuide` for iOS 11 and later
and falls back to a layout guide based on the frame rectangle of the current view for older systems.
```csharp
var insets = view.SafeAreaLayoutGuide();
```
The `SafeAreaLayoutGuide()` method uses the frame layout guide as a fallback.
The frame layout guide is a layout guide anchors of which simply return its owning view's anchors.
The frame layout guide is created using the `FrameLayoutGuide()` extension method
which is especially helpful for the scenarios like this:
```csharp
var isIos11OrLater = UIDevice.CurrentDevice.CheckSystemVersion(11, 0);
var widthAnchor = isIos11OrLater ? view.SafeAreaLayoutGuide.WidthAnchor : view.WidthAnchor;
var heightAnchor = isIos11OrLater ? view.SafeAreaLayoutGuide.HeightAnchor : view.HeightAnchor;
// more checks here...
```
To avoid checking for all anchors of the view you need, simple use the frame layout guide:
```csharp
var layoutGuide = isIos11OrLater ? view.SafeAreaLayoutGuide : view.FrameLayoutGuide();
```
This is exactly what the `SafeAreaLayoutGuide()` method does so you don't even need to check the system version.
But you can use the `FrameLayoutGuide()` method as a fallback for other layout guides introduced in iOS 11 if needed.

The initial call of the `FrameLayoutGuide()` method adds the returned layout guide to the view's layout guides,
so all subsequent calls return the already existing instance instead of creating a new one every time.

Note that the `FrameLayoutGuide()` method returns the `FrameLayoutGuide` property for `UIScrollView`.

The `SafeAreaInsets()` method returns the `UIVIew.SafeAreaInsets` for iOS 11 and later
and falls back to UIEdgeInsets.Zero for older systems.
```csharp
var insets = view.SafeAreaInsets();
```
The `AdjustedContentInset()` method returns the `UIScrollView.AdjustedContentInset` for iOS 11 and later
and falls back to `UIScrollView.ContentInset` for older systems.
```csharp
var insets = scrollView.AdjustedContentInset();
```

#### Update Watcher
This is a simple utility for a quick check for app updates in App Store.
```csharp
var updateWatcher = new UpdateWatcher(3); // Checks every 3 days
var updateAvailable = await updateWatcher.CheckForUpdateAsync();

if (updateAvailable)
    // Notify user
```

#### Machine Epsilon
As on some ARM devices both `float.Epsilon` and `double.Epsilon` equate to zero, the constants `MachineEpsilon.Single` and `MachineEpsilon.Double` are recommended to be used instead.

repository: rubo/missinkit

repo_name: rubo/missinkit


text:
# Using can-i-deploy with tags

The can-i-deploy tool was originally written to support specifying versions and dependencies using tags. This usage has now been superseded by first class support for [environments, deployments and releases](https://docs.pact.io/pact_broker/recording_deployments_and_releases/).

The following documentation covers the usage of can-i-deploy for users who are still using tags to track environments and deployments.

There are two ways to use `can-i-deploy` with tags. The first (recommended and most commonly used) approach is to specify just the application version you want to deploy and let the Pact Broker work out the dependencies for you. The second approach is to specify each application version explicitly. This would generally only be used if there were limitations that stopped you being able to use the first approach.

#### Specifying an application version

To specify an application (pacticipant) version you need to provide:

* the name of the application using the `--pacticipant PACTICIPANT` parameter,
* directly followed by *one* of the following parameters:
    * `--version VERSION` to specify a known application version (recommended)
    * `--latest` to specify the latest version
    * `--latest TAG` to specify the latest version that has a particular tag
    * `--all TAG` to specify all the versions that have a particular tag (eg. "all prod" versions). This would be used when ensuring you have backwards compatiblity with all production mobile clients for a provider. Note, when using this option, you need to specify dependency explicitly (see the second usage option).

Using a specific version is the easiest way to ensure you get an accurate response that won't be affected by race conditions.

#### Recommended usage - allowing the Pact Broker to automatically determine the dependencies

Prerequisite: if you would like the Pact Broker to calculate the dependencies for you when you want to deploy an application into a given environment, you will need to let the Broker know which version of each application is in that environment.

How you do this depends on the version of the Pact Broker you are running.

If you are using a Broker version where deployment versions are supported, then you would notify the Broker of the deployment of this application version like so:

    $ pact-broker record-deployment --pacticipant Foo --version 173153ae0 --environment test

This assumes that you have already set up an environment named "test" in the Broker.

If you are using a Broker version that does not support deployment environments, then you will need to use tags to notify the broker of the deployment of this application version, like so:

    $ pact-broker create-version-tag --pacticipant Foo --version 173153ae0 --tag test

Once you have configured your build to notify the Pact Broker of the successful deployment using either method describe above, you can use the following simple command to find out if you are safe to deploy (use either `--to` or  `--to-environment` as supported):

    $ pact-broker can-i-deploy --pacticipant PACTICIPANT --version VERSION
                               [--to-environment ENVIRONMENT | --to ENVIRONMENT_TAG ]
                               --broker-base-url BROKER_BASE_URL

If the `--to` or `--to-environment` options are omitted, then the query will return the compatiblity with the overall latest version of each of the other applications.

Examples:

Can I deploy version 173153ae0 of application Foo to the test environment?

    $ pact-broker can-i-deploy --pacticipant Foo --version 173153ae0 \
                               --to-environment test \
                               --broker-base-url https://my-pact-broker

Can I deploy the latest version of application Foo with the latest version of each of the applications it integrates to?

    $ pact-broker can-i-deploy --pacticipant Foo --latest \
                               --broker-base-url https://my-pact-broker

Can I deploy the latest version of the application Foo that has the tag "test" to the "prod" environment?

    $ pact-broker can-i-deploy --pacticipant Foo --latest test \
                               --to prod \
                               --broker-base-url https://my-pact-broker

#### Alternate usage - specifying dependencies explicitly

If you are unable to use tags, or there is some other limitation that stops you from using the recommended approach, you can specify one or more of the dependencies explictly. You must also do this if you want to use the `--all TAG` option for any of the pacticipants.

You can specify as many application versions as you like, and you can even specify multiple versions of the same application (repeat the `--pacticipant` name and supply a different version.)

You can use explictly declared dependencies with or without the `--to ENVIRONMENT_TAG`. For example, if you declare two (or more) application versions with no `--to ENVIRONMENT_TAG`, then only the applications you specify will be taken into account when determining if it is safe to deploy. If you declare two (or more) application versions _as well as_ a `--to ENVIRONMENT`, then the Pact Broker will work out what integrations your declared applications will have in that environment when determining if it safe to deploy. When using this script for a production release, and you are using tags, it is always the most future-proof option to use the `--to` if possible, as it will catch any newly added consumers or providers.

If you are finding that your dependencies are not being automatically included when you supply multiple pacticipant versions, please upgrade to the latest version of the Pact Broker, as this is a more recently added feature.

    $ pact-broker can-i-deploy --pacticipant PACTICIPANT_1 [--version VERSION_1 | --latest [TAG_1] | --all TAG_1] \
                               --pacticipant PACTICIPANT_2 [--version VERSION_2 | --latest [TAG_2] | --all TAG_2] \
                               [--to-environment ENVIRONMENT | --to ENVIRONMENT_TAG]

Examples:

Can I deploy version Foo version 173153ae0 and Bar version ac23df1e8 together?

    $ pact-broker can-i-deploy --pacticipant Foo --version 173153ae0 \
                               --pacticipant Bar --version ac23df1e8

Can I deploy the latest version of Foo with tag "master" and the latest version of Bar with tag "master" together?

    $ pact-broker can-i-deploy --pacticipant Foo --latest master \
                               --pacticipant Bar --latest master

Mobile provider use case - can I deploy version b80e7b1b of Bar, all versions of Foo with tag "prod", and the latest version tagged "prod" of any other automatically calculated dependencies together? (Eg. where Bar is a provider and Foo is a mobile consumer with multiple versions in production, and Bar also has its own providers it needs to be compatible with.)

    $ pact-broker can-i-deploy --pacticipant Bar --version b80e7b1b \
                               --pacticipant Foo --all prod \
                               --to prod

gha_language: Ruby

gha_license_id: MIT


text:
- Inline objects used as Array items will not have their properties copied

## Swagger/OAS 2
- Only the first entry in Produces / Consumes will be used as the content type. If there is no content type, but there is a schema, it will default to `application/json`
- The request body (first `in: body` in parameters[] is always required)
- All Query Parameters are strings
- Authentication is not currently imported

## Swagger/OAS 3
- Throws out `default` response (too ambiguous for a spec)
- examples will not be imported
- Only the first content-type for a response or `application/json` is used
- All Query Parameters are strings
- The following component types and references to them are not supported. We ran a check over all the APIs on API Guru and found actual usage of these ref types to be almost 0

    - parameters
    - examples
    - requestBodies
    - headers
    - securitySchemes
    - links
    - callbacks

- Authentication is not currently imported

repository: esopian/optic

repo_name: esopian/optic


text:
# Welcome to BADash

To contribute to this project or view the source, visit [github.com/swilcox/badash](https://github.com/swilcox/badash).

## Overview

BADash is designed as a simple dashboard for viewing a variety of data output elements from many different jobs.

Conceptually, there are 3 layers of organization:

1. _Event_ - a single point in time output of a job.
2. _Job_ - a task that is run somewhere and has output values.
3. _Dashboard_ - a collection of jobs that are grouped together for viewing purposes.

The output of a Job could be almost anything: raw text stdout/errout from a cronjob type job, a numerical reading from a sensor, map coordinates to something, etc. Every event from a Job has a result. A result of 0 implies no error and that the Job ran correctly. Beyond the result, any additional data can be used as a primary display field for that job.

## Project layout

    badash-api/       # The configuration file.
    badash-docs/      # The source of this documentation.
    badash-frontend/  # The vue.js source for the frontend

## API Installation

The API is written in Python using the Hug framework. To start developing or run the server locally, create a new python (Python 3.6.x) virtual environment, then:

    cd badash-api
    pip install -r requirements/local.txt

You will need a MongoDB database for persistence. The default localhost and collection name can be overridden via setting an environment variable:

    export MONGODB_URI=mongodb://localhost:27017/badash

The API can also be deployed to AWS Lambda via the Zappa package.

*TODO:* instructions on zappa (link to zappa) and a sample configuration file along with deployment command(s).

## Frontend Development

*TODO:* how to work with the Vue.js stuff.

repository: swilcox/badash

gha_language: Python

repo_name: swilcox/badash


text:
---
layout: post
title: Google Calendar to Google Sheets Bidirectional Sync
date: '2014-12-12T07:47:00.001-05:00'
author: Phillip Green II
tags:
- sheets
- javascript
- sync
- google calendar
- calendar
- google sheets
- google
modified_time: '2015-03-29T00:48:00.001-05:00'
blogger_id: tag:blogger.com,1999:blog-3096944600800047027.post-8062514110157538820
blogger_orig_url: http://coder-in-training.blogspot.com/2014/12/google-calendar-to-google-sheets.html
calendarUtilsLibProjectKey: MT9e6TqW6VlvXr43nv8kZqWRtpc-QmtJv
---

Personally, I love both spreadsheets and google calendar and some times it makes sense to keep them in sync.
The use case that started me on this was a calendar to keep track of important work dates.
Having things show up on my google calendar is great for keeping track of everything, but I have not found any simple way of doing bulk upload
My solution is to dump into a spreadsheet all of the events (holidays, major releases, ...).
There is one place to look.
I then added a menu to either push events from the spreadsheet to my calendar or vice versa.
The process is manual, but I haven't had any problems.
Generally, I view and update events through the calendar.
When necessary, I drop into the spreadsheet,
pull from the calendar, make my changes, then push back to the calendar.
Below is the code I use to make it happen.

##CalendarUtilsLib
###Creation
In order to re-use my sync code across multiple spreadsheets, I created a stand alone project that was reused as a library.
Go to https://drive.google.com/ and click
`New->More->Google Apps Script`

```javascript
//TODO: support day light savings time

function createEventsLookup(events){
  var lookup = {};
  events.forEach(function(event){
    lookup[event.getId()] = event;
  });

  return lookup;
}

function createEventInfosLookup(events){
  var lookup = {};
  events.forEach(function(event){
    lookup[event.getId()] = event;
  });

  return lookup;
}

function loadAllEventsFromCalendar(calendar) {
  return calendar.getEvents(new Date(2000,0,1), new Date(3000,0,1));
}
/*
* This throws in a pause (sleep) after so many steps.
* This exists because google will fail if you attempt too many updates at once.
*/
function throttleSpeed(limit,count) {
  if(count % limit === (limit - 1)) {
    Utilities.sleep(1000);
  }
}

function publish2calendar(calendar, eventInfos) {
  var existingEvents = loadAllEventsFromCalendar(calendar)
  var existingEventsById = createEventsLookup(existingEvents);

  var updatedEvents = {},
  updateCounts = 0,
  createdCounts = 0,
  errorsCount = 0;
  eventInfos.forEach(function(info,index){
    throttleSpeed(15,index);
    if(info && info.isValid()){
      var existingEvent = existingEventsById[info.getId()];
      if(existingEvent) {
        updatedEvents[existingEvent.getId()] = true;
        if(info.updateEvent(existingEvent)) {
          Logger.log('Updated Event: %s (%s %s-%s)', existingEvent.getId(),existingEvent.getTitle(),existingEvent.getStartTime(),existingEvent.getEndTime());
          updateCounts = updateCounts + 1;
        }
        } else {
          Logger.log('Creating Event: %s %s-%s',info.getTitle(), info.getStartTime(), info.getEndTime());
          var event = info.createEventOnCalendar(calendar);
          info.setId(event.getId());
          // Make sure the cell is updated right away in case the script is interrupted
          SpreadsheetApp.flush();
          createdCounts = createdCounts + 1;
        }
        } else {
          Logger.log('Invalid row[%s]: %s', index, info && info.toString());
        }
        })

        var deletedCounts = 0;
        existingEvents.forEach(function(event,index){
          if(!updatedEvents[event.getId()]) {
            throttleSpeed(15,index);
            Logger.log('Deleting Event: %s (%s %s-%s)', event.getId(),event.getTitle(),event.getStartTime(),event.getEndTime());
            event.deleteEvent();
            deletedCounts = deletedCounts + 1;
          }
          });

          var msgLines = [],
          timeout = 5;
          if(updateCounts > 0) {
            msgLines.push("Updates:\t" + updateCounts);
            timeout = -1
          }
          if(createdCounts > 0) {
            msgLines.push("New Events:\t "+ createdCounts);
            timeout = -1
          }
          if(deletedCounts > 0) {
            msgLines.push("Deleted Events:\t "+ deletedCounts);
            timeout = -1
          }
          if(errorsCount > 0) {
            msgLines.push("Errors:\t" + errorsCount);
            timeout = -1
          }

          if(msgLines.length === 0) {
            msgLines.push("No changes");
          }
          msgLines.forEach(function(line) {Logger.log(line)});
          SpreadsheetApp.getActiveSpreadsheet().toast(msgLines.join("\n"),"Publish Complete",timeout);
        }

        function refreshFromCalendar(calendar, eventInfos, addEvent) {
          var existingEvents = loadAllEventsFromCalendar(calendar);
          var infosById = createEventInfosLookup(eventInfos);

          var updateCount = 0,
          newEntriesCount = 0,
          errorsCount = 0;
          existingEvents.forEach(function(event){
            var info = infosById[event.getId()];
            if(info) {
              try{
                if(info.updateSelf(event)) {
                  Logger.log('Updated Info: %s (%s %s-%s)', info.getId(),info.getTitle(),info.getStartTime(),info.getEndTime());
                  updateCount = updateCount + 1;
                }
              }
              catch(e) {
                Logger.log(e.message);
                errorsCount = errorsCount + 1;
              }
              } else {
                info = addEvent(event);
                Logger.log('Created Info: %s (%s %s-%s)', info.getId(),info.getTitle(),info.getStartTime(),info.getEndTime());
                newEntriesCount = newEntriesCount + 1;
              }
              SpreadsheetApp.flush();
              });

              var msgLines = [],
              timeout = 5;

              if(updateCount > 0) {
                msgLines.push("Updates:\t" + updateCount);
                timeout = -1;
              }
              if(newEntriesCount > 0) {
                msgLines.push("New Entries:\t" + newEntriesCount);
                timeout = -1;
              }
              if(errorsCount > 0) {
                msgLines.push("Errors:\t" + errorsCount);
                timeout = -1;
              }

              if(msgLines.length === 0) {
                msgLines.push("No changes");
              }
              msgLines.forEach(function(line) {Logger.log(line)});
              SpreadsheetApp.getActiveSpreadsheet().toast(msgLines.join("\n"),"Refresh Complete",timeout);
            }
```

Save the above script as "CalendarUtilsLib".
Then click `File->Manage Versions...`.
Create the initial version (the comment doesn't matter).
Lastly, go to `File->Project Properties`.
On the page lists the "Project key".
It will look something like this: `{{ page.calendarUtilsLibProjectKey }}`.
The project key is what is necessary to import this script as a library.

###Explanation
CalendarUtilsLib exposes two functions: `refreshFromCalendar(calendar, eventInfos, addEvent)` and `publish2calendar(calendar, eventInfos)`.
The first is the pull function that takes events from the calendar and will add them to your spread sheet, the second is the push.
The first parameter in each function is the calendar object from the Google Calendar API, the second parameter is a list of EventInfos which is sourced from the spreadsheet.
The next paragraph goes into more detail.
Lastly, `refreshFromCalendar()` has a third parameter: `addEvent`.
This is actually a function that accepts a calendar event and will create a new `EventInfo` and add it to your spread sheet.
This function is called when an event exists on the calendar, but not in the spreadsheet.
An example implementation will be showed below.
CalendarUtilsLib works through an abstraction called `EventInfo`.
Essentially, `EventInfo`s allow you to customize how the calendar events map to your spread sheet.
`EventInfo`s must have three methods:
`updateSelf(calendarEvent)`, `updateEvent(calendarEvent)`, and `isValid()`.
The first updates the spreadsheet with the calendar event, the second updates the calendar event.
Each return a boolean of whether or not anything changes occurred.
The last method is used to keep garbage data from messing things up.
If the info says it isn't valid, then it won't be pushed to the calendar.
Below is sample implementations, so I won't go into detail here.

##WorkSchedule Script
Below is the spreadsheet code for the spreadsheet that I want to sync with a calendar.
I start by creating my script files and then import CalendarUtilsLib.

###Creation
Open the spreadsheet you want to sync with a calendar.
Click `Tools->Script Editor`.
Add the following code segments.
I split them into two scripts, but that is my personal preference.

####main.gs
```javascript
function retrieveDatesCalendar() {
  return CalendarApp.getCalendarsByName('Work')[0];
}

function loadAllInfos() {
  return loadWorkScheduleEventInfos();
}

function publish2calendar() {
  CalendarUtilsLib.publish2calendar(retrieveDatesCalendar(),loadAllInfos());
}

function refreshFromCalendar() {
  CalendarUtilsLib.refreshFromCalendar(retrieveDatesCalendar(),loadAllInfos(), addEventToWorkSheet);
}
/*This function adds the Menu options*/
function onOpen() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var menuEntries = [
{name: "Refresh (calendar -> sheet)", functionName: "refreshFromCalendar"} ,
{name: "Publish (sheet -> calendar)", functionName: "publish2calendar"}
];
ss.addMenu("Calendar", menuEntries);
}
```

####WorkScheduleEventInfo.gs
```javascript
function loadWorkScheduleEventInfos() {
  var sheet = SpreadsheetApp.getActive().getSheetByName('Work')
  var startRow = 2;  // First row of data to process
  var numRows = sheet.getLastRow();  // Number of rows to process
  var dataRange = sheet.getRange(startRow, 1, numRows, 8);
  return dataRange.getValues().map(function(row,index){
    return new WorkScheduleEventInfo(sheet,startRow+index,row);
  });
}

function addEventToWorkSheet(event) {
  var sheet = SpreadsheetApp.getActive().getSheetByName('Work');
  var rowNumber = sheet.getLastRow()+1;
  var info = new WorkScheduleEventInfo(sheet,rowNumber,[]);
  info.updateSelf(event);
  //This is a little tweak I added so I can recognize when new events are added to the sheet.
  var newRowAsRange = sheet.getRange(rowNumber + ":" + rowNumber);
  newRowAsRange.setBackground("red");
  return info;
}

function WorkScheduleEventInfo(sheet, rowNumber, rowData) {
  var COLUMN_TITLE_INDEX = 0;
  var COLUMN_DESCRIPTION_INDEX = 1;
  var COLUMN_LOCATION_INDEX = 2;
  var COLUMN_START_TIMESTAMP_INDEX = 3;
  var COLUMN_END_TIMESTAMP_INDEX = 4;
  var COLUMN_EVENT_ID_INDEX = 5;

  this.isValid = function() {
    return rowData[COLUMN_TITLE_INDEX] && rowData[COLUMN_START_TIMESTAMP_INDEX] && rowData[COLUMN_END_TIMESTAMP_INDEX];
  }

  function updateField(index,value) {
    sheet.getRange(rowNumber, index+1).setValue(value);
    rowData[index] = value;
  }

  this.getTitle = function() {
    return rowData[COLUMN_TITLE_INDEX];
  };

  this.setTitle = function(title) {
    updateField(COLUMN_TITLE_INDEX,title);
  }

  this.getDescription = function() {
    return rowData[COLUMN_DESCRIPTION_INDEX];
  };

  this.setDescription = function(description) {
    updateField(COLUMN_DESCRIPTION_INDEX,description);
  }

  this.getLocation = function() {
    return rowData[COLUMN_LOCATION_INDEX];
  };

  this.setLocation = function(location) {
    updateField(COLUMN_LOCATION_INDEX,location);
  }

  this.getStartTime = function() {
    return rowData[COLUMN_START_TIMESTAMP_INDEX];
  }

  this.setStartTime = function(startTime) {
    updateField(COLUMN_START_TIMESTAMP_INDEX,startTime);
  }

  this.getEndTime = function() {
    return rowData[COLUMN_END_TIMESTAMP_INDEX];
  }

  this.setEndTime = function(endTime) {
    updateField(COLUMN_END_TIMESTAMP_INDEX,endTime);
  }

  this.getId = function() {
    return rowData[COLUMN_EVENT_ID_INDEX];
  }

  this.setId = function(id) {
    updateField(COLUMN_EVENT_ID_INDEX,id);
  }

  this.createEventOnCalendar = function(calendar) {
    return calendar.createEvent(this.getTitle(), this.getStartTime(), this.getEndTime(), {description:this.getDescription(),location:this.getLocation()});
  }

  this.updateSelf = function(event) {
    var updateOccurred = false;
    if(this.getId() != event.getId()) {
      if(this.getId()) {
        throw Error('Cannot update self with event with different id');
        } else {
          this.setId(event.getId());
          updateOccurred = true;
        }
      }
      if(this.getTitle() != event.getTitle()) {
        this.setTitle(event.getTitle());
        updateOccurred = true;
      }
      if(!this.getStartTime() || this.getStartTime().getTime() != event.getStartTime().getTime()) {
        this.setStartTime(event.getStartTime());
        updateOccurred = true;
      }
      if(!this.getEndTime() || this.getEndTime().getTime() != event.getEndTime().getTime()) {
        this.setEndTime(event.getEndTime());
        updateOccurred = true;
      }
      if(this.getLocation() != event.getLocation()) {
        this.setLocation(event.getLocation());
        updateOccurred = true;
      }
      if(this.getDescription() != event.getDescription()) {
        this.setDescription(event.getDescription());
        updateOccurred = true;
      }

      return updateOccurred;
    }

    this.updateEvent = function(event) {
      if(this.getId() !== event.getId()) {
        throw Error('Cannot update event with different id');
      }
      changeOccurred = false;
      if(event.getTitle() != this.getTitle()) {
        event.setTitle(this.getTitle());
        changeOccurred = true;
      }
      if(event.getStartTime().getTime() != this.getStartTime().getTime() || event.getEndTime().getTime() != this.getEndTime().getTime()) {
        event.setTime(this.getStartTime(),this.getEndTime());
        changeOccurred = true;
      }
      if(event.getLocation() != this.getLocation()) {
        event.setLocation(this.getLocation());
        changeOccurred = true;
      }
      if(event.getDescription() != this.getDescription()) {
        event.setDescription(this.getDescription());
        changeOccurred = true;
      }
      return changeOccurred;
    }

    this.toString = function() {
      return this.rowData;
    }
  }
```

After adding the code, you import CalendarUtilsLib by clicking `Resources->Libraries`.
Paste the Project Key from earlier (`{{ page.calendarUtilsLibProjectKey }}`).
Be sure to select a version.
The development mode allows you to edit the library and test it at the same time, but it is slower and google suggests not doing it.
If you aren't making changes to CalendarUtilsLib, then disable development mode.
At this point, you are ready to tinker and customize for your code.
Read below for more details.

###Explanation

The code above assumes a worksheet in your spreadsheet called "Work" with the following columns:

  Title
  Description
  Location
  Start Timestamp
  End Timestamp
  Event Id (don't touch)

  Christmas Vacation

  Home
  12/24/2012 0:00:00
  12/28/2012 0:00:00

The event id column is very important.
This is what matches `EventInfo`s against Calendar events.
Because of that, it must exist and you should never manually change this column.

####main.gs
This script is where we setup the menu.
It needs to be able to retrieve the calendar (`retrieveDatesCalendar()`) and the infos (`loadAllInfos()`).
If your calendar is named differently, then update `retrieveDatesCalendar()`.
 I have two different spreadsheets that use CalenderUtilsLib and this portion of the script looks nearly the same in each.
 Pretty much the only difference is calendar name.
 The real customization occurs within the EventInfo.

####WorkScheduleEventInfo.gs
This is where you will be spending most of your tweaking.
I like to break my code into smaller chunks, which is why you see all of the setters and getters.
For me it makes the implementation of `updateEvent()`
and `updateSelf()` easier.
While the methods look big, each section is simply doing a comparison between event info and the calendar event to see if an update should occur.
 If so, it makes the change and marks that a change has occurred.
 The last bit of customization is in `addEventToWorkSheet()`.
 It works by creating a new `EventInfo` on the next blank line and doing an update.
 I have found this pattern works very well.
 For the sake of my own sanity, I color any new rows red.

##Conclusion
While there is a bit of setup work, I have used this pattern a couple times and it has really worked well for me.
 For my own spreadsheets, I have created varies `EventInfo` implementations where I some times dynamically generate fields.
 It has allowed a lot of flexibility.


text:
# Animal Crossing New Horizons Texture Generator

This is a set of functions to convert images into Animal Crossing New Horizons textures. This allows you to put your own images into the game as floor murals.

## There are two main functions to use:

#### img_to_animal_crossing_palette_info.py

This converts an image into a format that can be input into the game using the in-game color editor. This can be very tedious, but is capable of generating the highest-quality images and allow after-the-fact hand tuning if you want to modify the design.

#### img_to_animal_crossing_qrs.py

This converts an image into a format that can can be read into the game via QR codes mimicking the Animal Crossing New Leaf texture QR codes. The upsides is that this is very, very fast to get into the game. The downside is that there is a more restricted set of colors that can be used and the designs are unable to be edited once they are in the game.

## Requirements

These scripts require python 3 as well as the following packages:

numpy
scikit-learn
Pillow
PyQRCode

## Usage

### img_to_animal_crossing_palette_info.py

TODO

### img_to_animal_crossing_qrs.py

TODO


text:
# Project Title

This is an update on the Apache Sling JscpC Maven Plugin version 2.1.0 https://sling.apache.org/components/jspc-maven-plugin/index.html
Changes are for java 8 support, including dependendencies,  code and java version enforcer update.

## Getting Started

These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.

### Prerequisites

### Installing

                <plugin>
                    <groupId>org.apache.sling</groupId>
                    <artifactId>jspc-maven-plugin</artifactId>
                    <version>2.1.0-nga</version>
                </plugin>

## Built With

* [Maven](https://maven.apache.org/) - Dependency Management

## Contributing

Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us.

## Authors

## License

This project is licensed under the Apache License - see the original license here https://sling.apache.org/components/jspc-maven-plugin/index.html

repository: NationalGalleryOfArt/mvn-repo

repo_name: NationalGalleryOfArt/mvn-repo


text:
# Getting Started <!-- omit in toc -->

If you are viewing this file offline, the most up to date version of these instructions is located in the [project GitHub](https://github.com/lmco/lm-mit-momentum).

***ATTENTION STUDENTS:***  **THERE WAS AN UPDATE TO THE LIDAR REGISTRATION ALGORITHM**. If you are using the default templates or are relying on the LIDAR registration algorithm in them, the previous versions of these files contained a bug in the coordinate transforms. This bug has been fixed and you must update your files. Thanks to the student who pointed out the bug during office hours on 25 JAN.

**NOTES:**

1. Unless otherwise specified, all instructions are to be entered into the terminal in your Ubuntu installation.
2. Commands that start with `sudo` will require your user password. Using sudo invokes superuser security privileges and is akin to running an application as an administrator in Windows.
3. There is a [playlist](https://youtube.com/playlist?list=PLvn3cENh89AszwCOFpApcvQNTHO7Ap-us) showing successful execution of these commands.
4. If you have specific questions that are not answered by this document, check out the constantantly updating [Q&A](https://github.com/lmco/lm-mit-momentum/blob/master/QA.md).

## Table of Contents <!-- omit in toc -->
<!-- TOC and section numbers automatically generated, do not manually edit -->
- [1. Install Ubuntu 18.04 LTS or 20.04 LTS](#1-install-ubuntu-1804-lts-or-2004-lts)
  - [1.1. Basic steps to install Ubuntu outside of a Virtual Machine](#11-basic-steps-to-install-ubuntu-outside-of-a-virtual-machine)
  - [1.2. Basic steps to install Ubuntu inside of a Virtual Machine](#12-basic-steps-to-install-ubuntu-inside-of-a-virtual-machine)
- [2. Install VS Code IDE](#2-install-vs-code-ide)
- [3. Get Gazebo and PX4](#3-get-gazebo-and-px4)
  - [3.1. Clone PX4 and install Gazebo](#31-clone-px4-and-install-gazebo)
  - [3.2. Build PX4](#32-build-px4)
- [4. OPTIONAL Install QGroundControl](#4-optional-install-qgroundcontrol)
  - [4.1. Fix problem where PX4 running Gazebo can't connect to QGroundControl](#41-fix-problem-where-px4-running-gazebo-cant-connect-to-qgroundcontrol)
- [5. Install MAVSDK](#5-install-mavsdk)
- [6. Install navpy and numpy](#6-install-navpy-and-numpy)
- [7. Download py3gazebo](#7-download-py3gazebo)
- [8. Adding LM-provided LiDAR and terrain](#8-adding-lm-provided-lidar-and-terrain)
  - [8.1. Creating your own terrain and LiDAR](#81-creating-your-own-terrain-and-lidar)
- [9. Install py3gazebo](#9-install-py3gazebo)
- [10. Launch simulation](#10-launch-simulation)
  - [10.1. Set home position](#101-set-home-position)
  - [10.2. Launch PX4 with Gazebo](#102-launch-px4-with-gazebo)
  - [10.3. Set PX4 firmware parameters](#103-set-px4-firmware-parameters)
  - [10.4. How to find sensor topic name, message type, and get sample output](#104-how-to-find-sensor-topic-name-message-type-and-get-sample-output)
  - [10.5. OPTIONAL Launch QGroundControl](#105-optional-launch-qgroundcontrol)
- [11. Run a mission file](#11-run-a-mission-file)
- [12. Query sensor values using py3gazebo - GPS Example](#12-query-sensor-values-using-py3gazebo---gps-example)
- [13. Getting started with MAVSDK](#13-getting-started-with-mavsdk)
  - [13.1. Download MAVSDK examples](#131-download-mavsdk-examples)
  - [13.2. Run a MAVSDK example (PX4 and Gazebo have to be running)](#132-run-a-mavsdk-example-px4-and-gazebo-have-to-be-running)
<!-- TOC and section numbers automatically generated, do not manually edit -->

## 1. Install Ubuntu 18.04 LTS or 20.04 LTS

The software required for this project runs and is supported only in Ubuntu 18.04 or 20.04. As such, it is necessary to install this operating system to complete the project.

### 1.1. Basic steps to install Ubuntu outside of a Virtual Machine

1. [Download an Ubuntu image](https://ubuntu.com/download/desktop)
2. [Verify image download is not corrupted](https://ubuntu.com/tutorials/how-to-verify-ubuntu#1-overview)
3. Make a bootable live USB in current OS
    - Make live USB in [Windows](https://ubuntu.com/tutorials/create-a-usb-stick-on-windows#1-overview)
    - Make live USB in [Ubuntu](https://ubuntu.com/tutorials/create-a-usb-stick-on-ubuntu#1-overview)
    - Make live USB in [Mac](https://ubuntu.com/tutorials/create-a-usb-stick-on-macos#1-overview)
4. [Boot from created USB](https://ubuntu.com/tutorials/install-ubuntu-desktop#4-boot-from-usb-flash-drive)
   - If your computer doesn't automatically boot from from the USB and `F12` does not work to invoke the boot menu, pay attention to the bootscreen for the key specific for your computer.
5. Choose how to Install Ubuntu:
    - [Install to dual boot with current OS](https://ubuntu.com/tutorials/install-ubuntu-desktop#6-allocate-drive-space) (**recommended**). This will create an Ubuntu partition on your computer. Select "Install Ubuntu" when prompted after booting from USB, then [follow these steps](https://ubuntu.com/tutorials/install-ubuntu-desktop#6-allocate-drive-space)
    - [Install with persistent storage onto a USB drive](https://www.howtogeek.com/howto/14912/create-a-persistent-bootable-ubuntu-usb-flash-drive/) (**advanced**). This does not require any changes to your computer; instead Ubuntu will exist on its own USB device with its own memory. This will require an additional USB device to install the persistent image onto. Select "Try Ubuntu" when prompted after booting from USB, then [follow these steps](https://www.howtogeek.com/howto/14912/create-a-persistent-bootable-ubuntu-usb-flash-drive/)
    - [Install instead of current OS](https://ubuntu.com/tutorials/install-ubuntu-desktop#6-allocate-drive-space) (**not recommended**). This will replace your computer's OS with Ubuntu. Select "Install Ubuntu" when prompted after booting from USB, then [follow these steps](https://ubuntu.com/tutorials/install-ubuntu-desktop#6-allocate-drive-space)

### 1.2. Basic steps to install Ubuntu inside of a Virtual Machine

1. [Install VirtualBox](https://www.virtualbox.org/wiki/Downloads)
2. [Download an Ubuntu image](https://ubuntu.com/download/desktop)
3. [Verify image download is not corrupted](https://ubuntu.com/tutorials/how-to-verify-ubuntu#1-overview)
4. [Install Ubuntu in VirtualBox](https://brb.nci.nih.gov/seqtools/installUbuntu.html)

## 2. Install VS Code IDE

VS Code is an open-source light-weight feature-rich IDE (Integrated Development Environment) developed by Microsoft. Using an IDE greatly enhances the developer's comfort when coding by enabling features such as syntax highlighting, suggestions, and linting.

Summary of [VS Code setup instructions](https://code.visualstudio.com/docs/setup/linux):

``` sh
# Make the home directory for the project and step into it.
# You may install your software in a different directory, but then take care to change your home directory when following the instructions.
mkdir -p ~/Momentum
cd ~/Momentum

# Install VS Code from snap
sudo snap install --classic code

# Launch VS Code from current terminal directory and put it in the background
code . &
```

## 3. Get Gazebo and PX4

PX4 is industry-standard autopilot software for hobbyist drone applications. It provides easy access to high quality control laws for a variety of drones, including in simulation. For the purposes of this project, this software will allow the user to simply set waypoints, while the PX4 software performs all of the necessary calculations to control and interface with the motors to get the drone to the next waypoint.

Gazebo is similarly positioned in the robotics simulation world and is heavily used with ROS (Robot Operating System, not used in this project). Gazebo enables real-time physics simulation, sensor and terrain integration, and provides visual feedback for the user.

### 3.1. Clone PX4 and install Gazebo

Summary of [PX4 and Gazebo setup guide for ubuntu](https://dev.px4.io/master/en/setup/dev_env_linux_ubuntu.html):

``` sh
# Return to home project directory
cd ~/Momentum

# Make home directory for PX4
mkdir PX4
cd PX4

# Clone PX4
git clone https://github.com/PX4/PX4-Autopilot.git --recursive

# Get into the PX4 project directory
cd PX4-Autopilot

# Run installer script
bash ./Tools/setup/ubuntu.sh

#
# Log out and log back in.
#
```

### 3.2. Build PX4

Summary of [PX4 simulation with Gazebo](https://dev.px4.io/master/en/simulation/gazebo.html):

``` sh
# Get into the PX4 project directory
cd ~/Momentum/PX4/PX4-Autopilot

# Make the project with default drone target and gazebo simulation target
make px4_sitl gazebo
```

Take note:

- IP-address on the line that looks like `[Msg] Publicized address: <IP_ADDRESS>` will be useful if PX4 can't connect to QGroundControl when running Gazebo.
- IP-address on the line that looks like `[Msg] Connected to gazebo master @ http://<IP_ADDRESS>:11345` will be useful when setting up py3gazebo.

## 4. OPTIONAL Install QGroundControl

QGroundControl can be seen as a companion application to PX4. It provides a GUI interface for some of the most commonly used parameters in PX4. It also provides a contextualized view of the running mission. This software is not necessary to complete the project, but it may be useful for debugging.

Summary of [QGRoundControl installation instructions](https://docs.qgroundcontrol.com/master/en/getting_started/download_and_install.html):

``` sh
# Give yourself permissions to use the serial port
sudo usermod -a -G dialout $USER

# Remove modemmanager
sudo apt-get remove modemmanager -y

# Install dependencies
sudo apt install gstreamer1.0-plugins-bad gstreamer1.0-libav gstreamer1.0-gl -y

#
# Log out and log back in.
#

# Return to home
cd ~/Momentum

# Download QGroundControl app image
wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/latest/QGroundControl.AppImage

# Add execution permissions to the downloaded file
chmod +x ./QGroundControl.AppImage

# Launch app
./QGroundControl.AppImage # (or double-click)
```

### 4.1. Fix problem where PX4 running Gazebo can't connect to QGroundControl

Summary of [forum post solving this issue](https://discuss.px4.io/t/how-to-make-qgcontrol-connect-to-gazebo-simulation-instance-in-another-host-in-same-lan/9941):

``` sh
# Get into the PX4 project directory
cd ~/Momentum/PX4/PX4-Autopilot

# Edit the startup script
gedit ROMFS/px4fmu_common/init.d-posix/rcS
```

Add the IP address you see in the console when you run `make px4_sitl gazebo` to the line `mavlink start -x -u $udp_gcs_port_local -r 4000000` using the syntax

``` sh
-t 192.168.x.y
```

e.g.

``` sh
mavlink start -x -u $udp_gcs_port_local -r 4000000 -t <IP_ADDRESS>
```

## 5. Install MAVSDK

MAVSDK provides the Python hooks to interface with PX4. This allows the user to control the drone in a programmatic way, enabling complex missions and performing intricate algorithms.

Summary of [Python MAVSDK installation guide](https://github.com/mavlink/MAVSDK-Python#mavsdk-python):

``` sh
# Install MAVSDK Python library
pip3 install mavsdk
```

## 6. Install navpy and numpy

Navpy provides coordinate system conversion functions

``` sh
pip3 install numpy
pip3 install navpy
```

## 7. Download py3gazebo

Similar to MAVSDK, py3gazebo provides Python hooks to interface with Gazebo and its messages. This software is necessary in this project to read sensor data from Gazebo.

``` sh
# Step into project home directory
cd ~/Momentum

# Clone py3gazebo from GitHub and cd into the folder
git clone https://github.com/wil3/py3gazebo.git
cd py3gazebo

# Run 2to3 on the project
sudo apt-get install 2to3
2to3 -w *.py

# Replace all instances of deprecated `asyncio.async` with `asyncio.ensure_future`
sudo find ./ -type f -exec sed -i 's/asyncio.async/asyncio.ensure_future/g' {} \;

# Update proto definitions from the root of the py3gazebo project
export GAZEBO_HOME=/usr/include/gazebo-11
protoc --proto_path=$GAZEBO_HOME/gazebo/msgs --python_out=pygazebo/msg $GAZEBO_HOME/gazebo/msgs/*proto
```

The Python library is not yet installed. LM is providing an updated `setup.py` script that will be copied in the next step and there will be a prompt to install the library in [section 8](#8-install-py3gazebo).

## 8. Adding LM-provided LiDAR and terrain

Based on this [forum post](https://discuss.px4.io/t/create-custom-model-for-sitl/6700/2).

1. Download the LM provided assets from [this repository](https://github.com/lmco/lm-mit-momentum) and place parallel to your PX4 top folder

    ``` sh
    # Get into the project directory
    cd ~/Momentum

    # Clone project
    git clone https://github.com/lmco/lm-mit-momentum.git

    # Check directory tree
    sudo apt-get install tree
    tree -L 2
    ```

   - After downloading, your workspace directory should look like this (*otherwise the script will fail*):

        ``` tree
        .
         ├── lm-mit-momentum
         │   ├── ...
         ├── PX4
         │   └── PX4-Autopilot
         ├── py3gazebo
         │   └── ...
         └── ...
        ```

2. Run the bash script in the downloaded folder

    ``` sh
    # Step into the lm directory
    cd lm-mit-momentum

    # Add execution permissions to the script and execute
    chmod +x lm_setup.sh
    sudo ./lm_setup.sh
    ```

   - If you see the following printed to the terminal and no error messages, the script has succeded:

      ``` sh
      gazebo_iris_lmlidar__terrain2d
      gazebo_iris_lmlidar__terrain3d
      ```

### 8.1. Creating your own terrain and LiDAR

You should **not** need to create your own terrain or LiDAR for this project. If you are still looking for instruction on making your own assets, refer to [making_terrain_lidar.md](https://github.com/lmco/lm-mit-momentum/blob/master/making_terrain_lidar.md).

## 9. Install py3gazebo

Because there were specific changes required, the `lm_setup.sh` script had to copy a file over before you could install the library. To install the library, do the following:

``` sh
# Step into the py3gazebo folder
cd ~/Momentum/py3gazebo

# Install the Python library
pip3 install .
```

## 10. Launch simulation

### 10.1. Set home position

Setting the home position will ensure that Gazebo, PX4, and the Python mission are on the same page about where the drone is supposed to be.

``` sh
# Get into the PX4 project folder
cd ~/Momentum/PX4/PX4-Autopilot

# Source home position
source set_home.sh

# or do this from anywhere by hand
# https://dev.px4.io/master/en/simulation/gazebo.html#set-custom-takeoff-location
export PX4_HOME_LAT=0 # Deg
export PX4_HOME_LON=0 # Deg
export PX4_HOME_ALT=0 # Meters
```

### 10.2. Launch PX4 with Gazebo

Summary of [PX4 simulation using Gazebo](https://dev.px4.io/master/en/simulation/gazebo.html):

Launching PX4 and Gazebo (the `make...` command) will start the real-time simulation. From here, you can interact with the drone, send missions, move it in the world, and peek at the messages sent in and out of Gazebo.

```sh
# Get into the PX4 project folder
cd ~/Momentum/PX4/PX4-Autopilot

# Launch PX4 and Gazebo with the lidar and 2D terrain
make px4_sitl gazebo___terrain2d
```

### 10.3. Set PX4 firmware parameters

The PX4 firmware parameters set the constants used in the control laws of the autopilot. The following shows how to set the maximum velocities for the drone and load/save these parameters to file, as well as reset them to defaults.

``` sh
# Run from the shell (with pxh>) after launching PX4
# Set params manually
# https://dev.px4.io/master/en/advanced/parameter_reference.html
param set MPC_Z_VEL_MAX_DN 1.0 # m/s, max vertical velocity down
param set MPC_Z_VEL_MAX_UP 3.0 # m/s, max vertical velocity up
param set MPC_XY_VEL_MAX  12.0 # m/s, max horizontal velocity

# Or load from file (root location /PX4-Autopilot/build/px4_sitl_default/tmp/rootfs)
param load iris_defaults # Reset the parameters to when file was saved
param save # Optionally save params (not done automatically with load)

# Reset all params to default
param reset_all
```

### 10.4. How to find sensor topic name, message type, and get sample output

The following commands only work with Gazebo running and drone spawned in the simulation.

Get a list of all gazebo topics, filter for the word `scan`

``` sh
gz topic -l | grep scan
# /gazebo/default/iris_lmlidar/lmlidar/link/lmlidar/scan
```

Get the details on the lidar topic specifically

``` sh
gz topic -i /gazebo/default/iris_lmlidar/lmlidar/link/lmlidar/scan
# Type: gazebo.msgs.LaserScanStamped
#
# Publishers:
#   <IP_ADDRESS>:43455
#
# Subscribers:
#   <IP_ADDRESS>:46127
```

Echo the topic to the terminal - this will spam to your terminal, so press `Ctrl+C` to stop.

``` sh
gz topic -e /gazebo/default/iris_lmlidar/lmlidar/link/lmlidar/scan
# time {
#   sec: 12
#   nsec: 404000000
# }
# scan {
#   frame: "iris_lmlidar::lmlidar::link"
#   world_pose {
#     position {
#       x: 1.1076721818607924
#       y: 0.97990294068076067
#       z: -3.0112717917544356
#     }
#     orientation {
#       x: -9.72964361274114e-05
#       y: -0.0223892966197638
#       z: 0.000623484894630627
#       w: 0.99974912913033442
#     }
#   }
#   angle_min: -0.5236
#   angle_max: 0.5236
#   angle_step: 0.055115789473684208
#   range_min: 0.2
#   range_max: 10
#   count: 20
#   vertical_angle_min: -1.57
#   vertical_angle_max: 0
#   vertical_angle_step: 0.19625
#   vertical_count: 9
#   ranges: 0.34457796070222135
#   ...
#   ranges: 0.57489344102911
#   intensities: 0
#   ...
#   intensities: 0
# }

```

### 10.5. OPTIONAL Launch QGroundControl

Summary of [QGRoundControl installation instructions](https://docs.qgroundcontrol.com/master/en/getting_started/download_and_install.html):

``` sh
# Get into the project home directory
cd ~/Momentum/

# Launch QGroundControl
./QGroundControl.AppImage # (or double-click)
```

## 11. Run a mission file

``` sh
# Get into the folder with the mission file
cd ~/Momentum/lm-mit-momentum/demos

# Run mission file
python3 demo_mission.py
```

## 12. Query sensor values using py3gazebo - GPS Example

Get the available Gazebo topics and get the information on the topic of interest (take note of the message type). Gazebo and PX4 have to be running for this to work.

``` sh
gz topic -l | grep gps
# /gazebo/default/iris_lmlidar/gps0/link/gps

gz topic -i /gazebo/default/iris_lmlidar/gps0/link/gps
# Type: gazebo.msgs.GPS
#
# Publishers:
#   <IP_ADDRESS>:45247
#
# Subscribers:
```

Create a message subscriber class with a message callback and a way to poll the data when needed. Get your Gazebo Master IP-Address and Port from the following message when launching PX4 `[Msg] Connected to gazebo master @ http://<IP_ADDRESS>:11345`. The following is the same as in the file [demo_gps_read.py](https://github.com/lmco/lm-mit-momentum/blob/master/demos/demo_gps_read.py).

``` python
import time # For the example only
import asyncio
import pygazebo

# What you import here depends on the message type you are subscribing to
import pygazebo.msg.v11.gps_pb2

# This is the gazebo master from PX4 message
# `[Msg] Connected to gazebo master @ http://<IP_ADDRESS>:11345`
HOST, PORT = "<IP_ADDRESS>", 11345

class GazeboMessageSubscriber:

    def __init__(self, host, port, timeout=30):
        self.host = host
        self.port = port
        self.loop = asyncio.get_event_loop()
        self.running = False
        self.timeout = timeout

    async def connect(self):
        connected = False
        for i in range(self.timeout):
            try:
                self.manager = await pygazebo.connect((self.host, self.port))
                connected = True
                break
            except Exception as e:
                print(e)
            await asyncio.sleep(1)

        if connected:
            # info from gz topic -l, gz topic -i arg goes here
            self.gps_subscriber = self.manager.subscribe('/gazebo/default/iris_lmlidar/gps0/link/gps',
                                                         'gazebo.msgs.GPS',
                                                         self.gps_callback)

            await self.gps_subscriber.wait_for_connection()
            self.running = True
            while self.running:
                await asyncio.sleep(0.1)
        else:
            raise Exception("Timeout connecting to Gazebo.")

    def gps_callback(self, data):
        # What *_pb2 you use here depends on the message type you are subscribing to
        self.GPS = pygazebo.msg.v11.gps_pb2.GPS()
        self.GPS.ParseFromString(data)

    async def get_GPS(self):
        for i in range(self.timeout):
            try:
                return self.GPS
            except Exception as e:
                # print(e)
                pass
            await asyncio.sleep(1)

async def run():
    gz_sub = GazeboMessageSubscriber(HOST, PORT)
    asyncio.ensure_future(gz_sub.connect())
    gps_val = await gz_sub.get_GPS()
    # Simulate doing stuff and polling for the gps values only when needed
    start = time.time()
    current_time = 0
    last_time = 0
    while (current_time < 20):
        current_time = round(time.time() - start)
        if(current_time % 5 == 0 and last_time < current_time):
            gps_val = await gz_sub.get_GPS()
            print(gps_val)
            last_time = current_time
        if(current_time % 1  == 0 and last_time < current_time):
            print(current_time)
            last_time = current_time

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())
```

## 13. Getting started with MAVSDK

Follow the [MAVSDK quickstart guide](https://mavsdk.mavlink.io/develop/en/python/quickstart.html) to get a headstart on using MAVSDK with your setup. The [API reference](http://mavsdk-python-docs.s3-website.eu-central-1.amazonaws.com/) is useful for understanding how the examples work and for figuring out how to write your own code.

### 13.1. Download MAVSDK examples

``` sh
# Install subversion if not already installed
sudo apt install subversion

# Get into project home directory
cd ~/Momentum

# Download MAVSDK Python examples from the GitHub repository
svn checkout https://github.com/mavlink/MAVSDK-Python/trunk/examples
```

### 13.2. Run a MAVSDK example (PX4 and Gazebo have to be running)

``` sh
# Step into the examples folder
cd ~/Momentum/examples

# Launch example mission to takeoff, hover, then land
python3 takeoff_and_land.py
```

repository: JRetza/lm-mit-momentum

repo_name: JRetza/lm-mit-momentum


text:
# Processing controller

The Processing controller is a graphical desktop application for controlling
the parameters of a running Colordance via serial. It has graphical controls
like sliders, and outputs their values to a serial port when they change.

## Building and running

First, install [Processing](https://processing.org/download/) if you don't
already have it. You'll also need the
[ControlP5](http://www.sojamo.de/libraries/controlP5) library, which you can
install using Processing's library manager.

Open the sketch in Processing and run it from there.

repository: ademuri/colordance-circle

repo_name: ademuri/colordance-circle


text:
---
lang: en_US
title: "FutureRestore issues Help"
description: Provides solutions to some FR issues.
permalink: /futurerestore-help
redirect_from:
  - /frhelp
  - /futurerestorehelp
  - /FutureRestoreHelp
extra_contributors:
  - Tanbeer191
  - TheHacker894
---

## Help page

- This page is intended to provide solutions for errors encountered with FutureRestore. If none work, please join the r/Jailbreak [Discord Server](https://discord.gg/9apvC4C3CC) and ask in #futurerestorehelp

## iBEC error (error code -8)
This is a common error which you may run into while using FutureRestore, especially if you are using a Windows machine.
To fix this:
1. Uninstall all Apple devices in Device Manager
    - Make sure to check "Uninstall Driver" when uninstalling
1. Uninstall all Apple products including iTunes and Apple Device Support programs in the Control Panel
1. Unplug and replug your device
1. Check device manager for any Apple devices and uninstall them and any drivers
1. Disconnect your device
1. Install the iTunes `.exe` file from [apple.com](https://support.apple.com/kb/DL1816?locale=en_US) itself
1. Boot your device into [Recovery Mode](https://www.howtogeek.com/396530/how-to-put-your-iphone-or-ipad-into-recovery-mode/) and plug it into your computer
1. Uninstall the iBoot (Recovery) driver and software from Device Manager
1. Run FutureRestore again
1. Unplug and replug your device.
1. Run FutureRestore again. This issue should now be resolved.

![example](https://media.discordapp.net/attachments/825122925204078622/839186681202212904/unknown.png)

## Device ApNonce doesn't match APTicket nonce

This error means that you have not set your generator on your device to that of the blob. In order to solve this problem, follow the `Setting nonce` part of the <router-link to="/futurerestore">FutureRestore page</router-link>

## SEP Firmware is not being signed

This problem occurs when the user tries to manually specify SEP from the *target* version, instead of from the *latest* available version. To fix this problem, either choose the `latest-sep` argument or use the `custom-latest` or `custom-latest-buildid` with the `custom-latest-beta` argument with a compatible version provided based on the [SEP/BB Compatibility Chart](https://docs.google.com/spreadsheets/d/1Mb1UNm6g3yvdQD67M413GYSaJ4uoNhLgpkc7YKi3LBs/edit#gid=0) (you can use [https://appledb.dev](appledb) to find the buildid for a version). You do not need to manually provide SEP or BB.

## Could not connect to device in recovery mode / Failed to place device in recovery mode

**NOTE:** if the error is similarly named, follow these steps too.

- If your device is in recovery mode, run FutureRestore again while your device is in recovery mode.
- If your device is not in recovery mode, enter recovery mode manually, then run FutureRestore again.

## Error: Unable to receive message from FDR...

The fix for this is either waiting (it can take a very long time) or just re-trying the process.
This is an error that has been diagnosed but no fix for it is available as of the time of writing this.

## About to send NOR data... No data to read

If you get this error message, you may try stopping the futurerestore process and disabling the `no-rsep` argument. If the restore continues to get stuck here regardless if `no-rsep` is enabled or disabled, you may be unable to restore as the cause of this error has not been diagnosed at the time of writing.

branch_name: refs/heads/main


text:
# Azure IoT Central and MXChip HOL
> NOTE: This HOL makes use of the IoT Central legacy application templates. It is archived and will no longer be maintained.

In this lab you can experience the simplicity of SaaS for IoT, with no cloud expertise required.
Azure IoT Central is a fully managed IoT SaaS (Software-as-a-Service) solution that makes it easy to connect, monitor and manage IoT assets at scale.
Using IoT Central gives you the following advantages:
- Simplify setting up your IoT solution
- Connect and manage your things with ease
- Built-in world-class security and privacy

More information about Azure IoT Central, including a getting started video,
can be found in the [Azure IoT Central documentation](https://docs.microsoft.com/en-us/previous-versions/azure/iot-central/core/overview-iot-central).
## Azure IoT Central Personas
In this workshop you will learn how to build, use and administer an IoT Central application through the perspective of each of the following personas:
1) Administrator
   - Create IoT Central applications
   - Administer IoT Central applications
1) Builder
   - Define new device types
   - Configure rules and actions
   - Customize the operator's view
1) Operator
   - Monitor devices
   - Add new device instances

## Creating an Azure IoT Central Application
> NOTE: The actual screen shot to create an application will be different. You need to make sure to select a **Custom Application (Legacy)** template.

This tutorial show you, as an administrator, how to create a new Azure IoT Central application. Navigate to the [Azure IoT Central application manager](https://aka.ms/iotcentral) website. Then sign in with a Microsoft personal, work, or school account.
To start creating a new Azure IoT Central application, select **New Application**. This link takes you to the **Create an application** page.

![ScreenShot](Images/IOTC-01.jpg)

For the purpose of this workshop, use the following settings:
- Choose payment plan **Trial**
- Select an application template **Sample Devkits**
- Set an **Application name** e.g. *Sample Devkits IoT HOL 001*. This name has to be unique.
- Provide all mandatory contact information
- Click **Create** to create your new Azure IoT Central application

More details on how to create an Azure IoT Central application and the meaning of all different fields can be found [in this document](https://docs.microsoft.com/en-us/azure/iot-central/howto-create-application).

An application created from the **Sample Devkits** application template includes a **MXChip** device template that defines a large number of device characteristics, utilizing the sensors on the device. More details about the configuration can be found in the [MXChip Device template details](https://docs.microsoft.com/en-us/azure/iot-central/howto-connect-devkit#mxchip-device-template-details).

## Add a real device to your Azure IoT Central application
Follow [these step-by-step instructions](https://docs.microsoft.com/en-us/previous-versions/azure/iot-central/core/howto-connect-devkit#add-a-real-device) to add a physical device to your IoT Central application, to install the latest firmware on the MXChip DevKit, to setup WiFi and to connect the device securely to the IoT Central application.
Work to the entire instruction set to see the device's telemetry data and to control the device. Optionally you can also take a look at the device firmware, as described in [this part of the document](https://docs.microsoft.com/en-us/previous-versions/azure/iot-central/core/howto-connect-devkit#review-the-code).

## Use IOTC Explorer to monitor telemetry data
Use the IoT Central Explorer CLI to see messages your devices are sending to IoT Central and observe changes in the IoT Hub twin. You can use this open-source tool to gain deeper insight into the state of device connectivity and diagnose issues of device messages not reaching the cloud or devices not responding to twin changes.
### Prerequisites
- [Node.js version 8.x](https://nodejs.org) or higher
- An administrator of your application must generate an access token for you to use in iotc-explorer
### Install iotc-explorer
Run the following command from your command line to install:
``` bash
npm install -g iotc-explorer
```
### Run iotc-explorer
The following sections describe common commands and options that you can use when you run **iotc-explorer**. To view the full set of commands and options, run the following command from your command line:
``` bash
iotc-explorer --help
```
#### Login
Before you can do anything meaningfull with iotc-explorer, you need to have an adminstrator of your IoT Central application to get an access token for you to use. Take the following steps to generate the access token:
1) Navigate to **Administration** followed by **Access Tokens**
1) Select **Generate Token**

![ScreenShot](Images/IOTC-02.jpg)

Enter a Token name, select **Next** and then **Copy**. On the next screen click on **Generate**.

![ScreenShot](Images/IOTC-03.jpg)
![ScreenShot](Images/IOTC-04.jpg)

> NOTE: The token value is only shown once, so it must be copied before closing the dialog. After closing the dialog, it is never shown again.

For this exercise, just copy the token to Notepad for later use.

Open a command prompt and login to iotc-explorer:

![ScreenShot](Images/IOTC-05.jpg)

Notice the different commands you can give when calling iotc-explorer and at least make sure to monitor telemetry messages, received from your connected MXChip device.

![ScreenShot](Images/IOTC-06.jpg)

This concludes the HOL on a physical MXChip device connected to an Azure IoT Central application. This is a great starting point for exploring more options of Azure IoT Central. To see what else is possible, please read the [Azure IoT Central Documentation and Quickstarts](https://docs.microsoft.com/en-us/previous-versions/azure/iot-central/core/overview-iot-central).

repository: IoTForDevices/IoT-Various-HOLs

repo_name: IoTForDevices/IoT-Various-HOLs


text:
---
navtitle: Developer
sort: 5
---

# Developer information

You are encouraged to [contribute](/project/contribute/) to Nikita. There are multiple ways to offer assistance to the project. To fix and write actions, you will have to get your hands dirty and dive into the source code. This page describes how to setup your developer and testing environment and how tests are organized and executed.

Before starting with Nikita's development, read the [project architecture](/project/architecture/), which presents how to project is organized in the Git repository.

## Tests execution

Nikita targets Unix-like systems including Linux and macOS. Windows is not supported as a targeting node where actions are executed. It is however known to work as a Nikita host. This means you can run Nikita from a Windows host and target Linux nodes over SSH.

Nikita use [Mocha](https://mochajs.org/) and [Should.js](https://shouldjs.github.io/). All tests are all located inside the `test` folders.

### Running all tests

The command `yarn test` run both on the local machine as well as on containerized and virtualized environments to simulate various operating systems and execution contexts. Lerna can run multiple commands in parallel with the `--concurrency` option:

```
npx lerna run test --stream --concurrency 8
```

### Running local tests

For the local tests to execute successfully, you must:

* be able to ssh yourself (eg `ssh $(whoami)@localhost`) without a password
* be online (attempt to fetch an FTP file)

Local tests are executed with the command `yarn test:local`. It can be executed globally from the project root directoy or selectively inside each package. Inside a package, to speed test execution, you can pass the `--parallel` Mocha argument, for example:

```bash
cd packages/core
yarn run test:local --parallel
```

To execute a subset of the tests, go to the package folder and pass a globbing expression to Mocha such as `cd ./packages/{package} && npx mocha 'test/**/*.coffee'`.

For example, to only test the `nikita.file.ini` actions, run the following:

```bash
cd packages/file && npx mocha test/ini.coffee
# Or
yarn workspace @nikitajs/file exec mocha test/ini.coffee
# Or
npx lerna --scope @nikitajs/file exec mocha test/ini.coffee
```

### Running environment tests

For the environment tests to execute successfully, you must:

* Install and configure a [Docker client](https://www.docker.com/).
* Install and configure an [LXD client](https://linuxcontainers.org/lxd/introduction/).

Environment tests are executed with the command `yarn test:env`. To execute a single environment, go to the targeted `env` folder such as `./packages/{package}/env/{env}` and execute its `run.sh` script.

## Configuration customization

Tests load a configuration file. Its purpose is to parameterize the Nikita sessions and to filter the execution of tests with a tagging system.

By default, the configuration files are located in each package folder. They are named `test.coffee`. A sample file is provided as well named `test.coffee.sample`. If a configuration file does not exists, the sample file is copied.

You can also customize the path to the configuration module by setting the `NIKITA_TEST_MODULE` environmental variable.

## SSH or locally

Why even choose? When it makes sense, the majority of tests run twice. Once without an SSH connection and once with an SSH connection pointing to `localhost` by default. To achieve this behavior, we extended [Mocha](https://mochajs.org/) by providing an alternative to the `it` function in the name of `they`. You can find it in the [mocha-they package](https://github.com/adaltas/node-ssh2-they).

For example, this test will only be executed locally:

```js
const nikita = require('nikita');

describe('Simple Test', () => {
  it('Check a file is touched', () => {
    nikita
    .file.touch('/tmp/a_file')
    .fs.assert('/tmp/a_file')
  })
})
```

While the same test using `they` will be executed locally and remotely using provided configuration:

```js
const nikita = require('nikita');
const {config} = require('./test');
const they = require('mocha-they')(config);

describe('Simple Test', () => {
  they('Check a file is touched', (ssh) => {
    nikita({$ssh: ssh})
    .file.touch('/tmp/a_file')
    .fs.assert('/tmp/a_file');
  })
})
```

## Environments

Some tests depend on particular settings to run successfully. Some actions are specific to a particular Linux distribution or issue internally alternatives commands which must be validated. Other actions depend on a service that is not always available on the host machine such as a database connection.

The environment tests require the presence of [Docker](https://www.docker.com/) and [LXD](https://linuxcontainers.org/lxd/introduction/) clients.

Based on your environment support, targeted tests can be activated from the configuration. Tests are labeled with tags. The environment defines the test coverage by activating tags in their `test.coffee` configuration file. For example, to activate the MariaDB tests located in the ["db" package](https://github.com/adaltas/node-nikita/blob/master/packages/db/env/mariadb/test.coffee), set the `tags.db` property to `true` and configure the `db.mariadb` properties accordingly.

## Docker

To ensure tests are executed in a proper environment, we leverage [Docker](https://docs.docker.com/) and [Docker Compose](https://docs.docker.com/compose/). To each environment corresponds a directory inside the "./env" folder. Inside each folder, you will find the "docker-compose.yml" declaration file and its associated resources.

- `docker-compose.yml`
  The [Docker Compose](https://docs.docker.com/compose/) file declares the Nikita container with its test environment as well as its service dependencies such as databases services.
- `Dockerfile`
  The [Dockerfile](https://docs.docker.com/engine/reference/builder/) declares instructions to build the containers.
- `test.coffee`
  The configuration file is used to activate selected tests and configured the Nikita sessions executed inside.

The commands to execute the tests are common to every Docker environment and provide a lot of flexibility. From any environment directory, you can run:

* `docker compose up --abort-on-container-exit`
  Run the all test suite from the host shell.
* `docker compose run --rm nodejs`
  Enter inside the Nikita container and execute your commands.
* `docker compose run --rm nodejs 'test/**/*.coffee'`
  Run a subset of the tests from the host shell.

 The tests require a local LXD client. To install it on a Linux host, you can follow the [installation instructions](https://linuxcontainers.org/lxd/getting-started-cli/). On non-Linux hosts, you can set up the client to communicate with a remote LXD server hosted on a virtual machine. However, you will have to mount the project directory into the "/nikita" folder of the virtual machine. The provided [Vagrantfile](https://github.com/adaltas/node-nikita/blob/master/packages/lxd/assets/Vagrantfile) definition inside the "packages/lxd/assets" folder will set you up.

For Windows and macOS users, the procedure is abstracted inside the `./bin/cluster start` command:

```bash
# For Windows and macOS users
cd packages/lxd
./bin/cluster start
yarn test
```

The manual commands to make it work are below:

* Install:

```bash
# Initialize the VM
cd packages/lxd/assets && vagrant up && cd ../../
# Set up LXD client
lxc remote add nikita <IP_ADDRESS>:8443
lxc remote switch nikita
# Navigate to the target environment
cd ipa/env/ipa
# Initialize the container
npx coffee start.coffee
```

* Update the VM:

```bash
lxc remote switch local
lxc remote remove nikita
# Note, password is "secret"
lxc remote add nikita <IP_ADDRESS>:8443
lxc remote switch nikita
```

If you are running into an issue with permission on the "tmp" folder as below:

```bash
[1/29]: configuring certificate server instance
[error] IOError: [Errno 13] Permission denied: '/tmp/tmp_Tm1l_'
```

Host must have `fs.protected_regular` set to `0`r, eg `echo '0' > /proc/sys/fs/protected_regular && sysctl -p && sysctl -a`. In our Physical -> VM -> LXD setup, the parameters shall be set in the VM, no restart is required to install the FreeIPA server, just uninstall it first with `ipa-server-install --uninstall` before re-executing the install command.

Here's a complete example to run tests for the ["ipa" package](https://github.com/adaltas/node-nikita/tree/master/packages/ipa):

```bash
# For Windows and macOS users
./packages/lxd/bin/cluster start
export NIKITA_HOME=/nikita
cd packages/ipa
# Start the server
npx coffee ./env/ipa/start.coffee
# Run all the tests
lxc exec nikita-ipa --cwd /nikita/packages/ipa npx mocha 'test/**/*.coffee'
# Run selected tests
lxc exec nikita-ipa --cwd /nikita/packages/ipa npx mocha 'test/user/exists.coffee'
# Enter the IPA container
lxc exec nikita-ipa --cwd /nikita/packages/ipa bash
```

repository: adaltas/node-nikita

gha_language: CoffeeScript

repo_name: adaltas/node-nikita


text:
---
layout: project
title: "Masterpieces of Indian Art & Architecture"
description: This resource offers an introduction to 2000 years of art on the Indian subcontinent.
link: "http://learn.columbia.edu/indianart/"
img: indian-art.png
author: Alex Gil
archive: True
people:
- Vidya Dehejia
teams:
- Media Center for Art History
---

This resource offers an introduction to 2000 years of art on the Indian subcontinent. It is divided into segments devoted to the art (and architecture) of Buddhism, the emergence and development of the Hindu temple, the art of Islamic sultanates, Mughal and sub-imperial rule, early modern and British colonial art, and the art of post-colonial and contemporary India. Visual materials includes architectural sites, sculptures, painted manuscripts, paintings, and photography and are meant to be a companion to survey lectures on Indian Art.

repository: cul/dlst-dh

gha_language: HTML

repo_name: cul/dlst-dh


text:
# Getting Started with Binoculars

This Getting Started Guide will walk you through the steps to download, install, and configure Binoculars for your version of Dynamo.

To keep it simple we're assuming you want to use Google Sheets to store the log data recorded from Binoculars and then visualise it within Google Data Studio.

1. [Download Binoculars](#download-binoculars)
2. [Create a Google Cloud Platform Service Account](#create-a-google-cloud-platform-service-account)
3. [Create a Google Sheet](#create-a-google-sheet)
4. [Enable the Google Sheets API](#enable-the-google-sheets-api)
5. [Update Binoculars settings.json](#update-binoculars-settings-json)
6. [Test Binoculars](#test-binoculars)
7. [Setup Google Data Studio](#setup-google-data-studio)
8. [Conclusion](#conclusion)

## Download Binoculars

Download the latest release of Binoculars from [releases](https://github.com/teamtreedyn/Binoculars/releases).

Extract the `Binoculars` folder and copy it to the packages folder for the version of Dynamo or Dynamo Revit you use.

![](Images/getting-started/0.5/14-package-settings.png)

If you just use Dynamo Sandbox or Dynamo for FormIt this could be either:
```
%appdata%\Dynamo\Dynamo Core\2.0\packages\
%appdata%\Dynamo\Dynamo Core\2.1\packages\
```

And if you run Dynamo from Revit try:
```
%appdata%\Dynamo\Dynamo Revit\2.0\packages\
%appdata%\Dynamo\Dynamo Revit\2.1\packages\
```

If you use Dynamo in multiple contexts then copy the package to each directory.

## Create a Google Cloud Platform Service Account

First we need to set up a Google Cloud Platform Service Account to authenticate access to Google Sheets.

Open the following link:
[http://console.cloud.google.com/iam-admin/serviceaccounts/create](http://console.cloud.google.com/iam-admin/serviceaccounts/create)

![](Images/getting-started/0.5/01-cloud-terms.png)

Agree to the terms and conditions.

![](Images/getting-started/0.5/02-cloud-create-project.png)

![](Images/getting-started/0.5/03-cloud-new-project.png)

Create a project. You can call it anything you like.

![](Images/getting-started/0.5/04-cloud-create-service-account.png)

Now, create a Service Account.

If you've ended up on a different page then just follow the link again:
[http://console.cloud.google.com/iam-admin/serviceaccounts/create](http://console.cloud.google.com/iam-admin/serviceaccounts/create)

You can call it anything you like but keep it simple.

![](Images/getting-started/0.5/05-cloud-service-account-permissions.png)

Now, you'll be asked to assign permissions to the Service Account. We don't want to add any so just press continue.

![](Images/getting-started/0.5/06-cloud-service-account-grant.png)

Now, we don't want to Grant any users access to this Service Account but we do want to create a key.

![](Images/getting-started/0.5/07-cloud-service-account-key.png)

Select JSON for the key type.

![](Images/getting-started/0.5/08-cloud-service-account-key-dl.png)

The key will now be downloaded. We don't need it just yet so save it somewhere memorable.

Click done to return to the Service Accounts index on Google Cloud Platform.

![](Images/getting-started/0.5/09-cloud-service-account-email.png)

You should now see the Service Account we've just created. Copy the email address to your clipboard.

## Create a Google Sheet

Next we will create the Google Sheet where your data will be stored.

Open Google Drive with the following link.
[https://drive.google.com/drive/](https://drive.google.com/drive/)

![](Images/getting-started/0.5/10-drive-new.png)

Create a new Google Sheet.

![](Images/getting-started/0.5/11-sheets-share.png)

Press Share.

![](Images/getting-started/0.5/12-sheets-name.png)

Give the document a name.

![](Images/getting-started/0.5/13-sheets-email.png)

Now paste the email address for the Google Cloud Platform Service Account and press send.

That's great! Our Service Account can now edit the Google Sheet.

## Enable the Google Sheets API

Now we need to enable the Google Sheets API on your Google Cloud Console Account.

![](Images/getting-started/0.5/13b-google-sheets-api.png)

Just open the following link and press enable:
[https://console.developers.google.com/apis/api/sheets.googleapis.com/overview](https://console.developers.google.com/apis/api/sheets.googleapis.com/overview)

## Update Binoculars settings.json

It's now time to edit `settings.json`.

![](Images/getting-started/0.5/14-package-settings.png)

Return to the directory you saved the Binoculars package to and open the `settings.json` file within it.

![](Images/getting-started/0.5/15-settings-default.png)

I've opened it in Visual Studio Code here but you can just as easily edit it in Notepad or another text editor.

Scroll down to the `export` section.

![](Images/getting-started/0.5/16-settings-updated.png)

Copy the Google Sheet `id` from the url of your Google Sheet file as seen in the screenshot above.

Now add the `sheet` name as seen in the screenshot above. Assuming you haven't changed it, this is probably just `Sheet1`.

Finally, copy and paste the values from the Service Account credentials `.json` file we downloaded earlier into the `googleSheetsServiceAccount` section.

The file should now look similar to this image above. I've blacked out some details which should be kept secret.

Save the `settings.json` file.

Be sure to copy your `settings.json` changes to all the other contexts you use Dynamo as shown in the first step.

## Test Binoculars

Binoculars should now be configured so open up Dynamo. You should now see an icon for Binoculars on the menu bar.

![](Images/getting-started/0.5/17-binoculars-sheets.png)

You can open the Google Sheet from Dynamo by pressing the Binoculars icon in the menu bar and choosing Google Sheets. Do that now and then run a script or graph in Dynamo and check that the data is correctly logged to the Google Sheet.

## Setup Google Data Studio

Open the Google Sheet one more time.

Now we need to add a header row to the data.

![](Images/getting-started/0.5/18-sheets-header.png)

Insert a new row with the following values

```
Username	Computer	IP	Geolocation	City	Country	Dynamo Version	Revit Version	Graph Name	Date	GUID
```

Open the example Google Data Studio Data Source with the following link:
https://datastudio.google.com/open/1f31CffnWADHxDqb_ofQmYl8ZDO6hpkSb

![](Images/getting-started/0.5/20-data-source-edit.png)

Click the Make a Copy icon in the top right hand corner. Then click Edit Connection on the left.

Authorise Data Studio to Access your Google Sheets.

![](Images/getting-started/0.5/21-data-source-connection.png)

Select the Spreadsheet we created earlier and then press Reconnect in the top right hand corner.

![](Images/getting-started/0.5/22-data-source-connection-changed.png)

*Please note: The template hasn't yet been configured to perfectly match the current data Binoculars is reporting. My test has shown that linking the two works but you may see a similar warning as shown above. For now just click Apply, cross your fingers and hope for the best. That worked for me.*

![](Images/getting-started/0.5/23-data-source-complete.png)

Great, the data should now be connected. You can press Explore to check Data Studio is correctly reading from the Google Sheet but lets skip that for the moment and create the Report.

Open the example Google Data Studio Report with the following link:
https://datastudio.google.com/u/0/reporting/14WNMimtt4muq1A_b4Pk8Iy7HiXPkGeKY

![](Images/getting-started/0.5/25-data-report-source.png)

Click Make a Copy in the top right hand corner and make sure to select the Data Source we just setup - not the existing "Binoculars Dashboard - Public".

That's it! We now have Binoculars linked to Google Data Studio.

The charts and visuals might look a bit bare at the moment because we haven't got much data to read.

Either run a few different scripts in Dynamo to generate some data or copy and paste some sample data into your Google Sheet.

![](Images/getting-started/0.5/26-data-report-date.png)

Be sure to change the Date Range on the Google Data Studio Report to ensure you're correctly filtering the data.

One final step. Copy the id of the Google Data Studio Report in to `settings.json` just as we did for the Google Sheet. This will enable the ability to open Data Studio from inside Dynamo!

# Conclusion

We hope you found this Getting Started Guide easy to follow. If it didn't work for you then please get in touch and we'll try to help you out and improve our documentation for the future!

If you continue to use Google Data Studio then please share any interesting stats and visuals you're able to extract. There's a wealth of data to be explored through Binoculars and our template barely touches the surface.

repository: teamtreedyn/Binoculars

gha_language: C#

repo_name: teamtreedyn/Binoculars