Tuesday, May 21, 2019

Interview Questions for Older Relatives for Genealogy Research

My Great Grandfather, Pickens Steele Fincher
During my recent Sabbatical from work I have been researching my family tree. I've interviewed some relatives about their lives which has been enlightening. I found a few sites on the web that have questions to ask older relatives. Here's some of my favorite ones:

 https://www.deseretnews.com/article/865595932/Genealogy-150-questions-to-ask-family-members-about-their-lives.html

http://sites.rootsweb.com/~ohlths/interview2.htm

 https://www.thoughtco.com/fifty-questions-for-family-history-interviews-1420705

 If your parents, or grandparents, are still alive ask them some of these questions and record your conversation on your phone. They will be gone before you know it.

You can see more photos about the Fincher clan at https://www.fincher.org/fincher/index.shtml

Tuesday, April 16, 2019

Agile Austin - Why 76% Of Organizations Are Failing At Agile

Why 76% Of Organizations Are Failing At Agile

David Hawks from AgileVelocity.com gave a great presentation to 50 Agilists at Agile Austin this week.

He used an interactive poll from PollEveryWhere.com which was a nice touch, so the audience could vote in real time on a question and we could see the results in a bar chart.

Four phases of change:

1. "Status Quo"

2. "Chaos and Resistance" When we introduce change the process always get worse.

3. "Integration and Practice" is next in which things slowly get better.

4. "New Status Quo" where things are genuinely better.

Four Agile Steps:

  1. Align

    Why are we going to Agile? It's not just to "do" Agile. Goal should be something like improve time to market, improve productivity, make happier customers, and have better quality.

  2. Learn

    Everyone needs to understand Agile.

  3. Predict

    We need to make things predictable before going faster.

  4. Accelerate

    This is where we actually start getting better.

  5. Adapt

    Try new things like Experiment Based Development

David had us do a small task in groups.

Why do so many companies struggle?

Most company think of Agile as a team level problem. Upper management just wants the team level to change, but not anything else. We need to change the System. We need to change the Organization, the leadership needs to understand Agility.

Three Impediments to Successful Agile Transformation

  1. Implementing solutions without clarity in desired results

    This produces:
    Lots of resistance and frustration
    Silo Optimization
    No clear measure of success

    We did another exercise on how the importance of different tasks to transform a culture.

    Steps to Organizational Change

    a. Create Urgency - "We have to get better,
        or our company may go bust."  (Comes for the top level)
    b. Form a powerful coalition
    c. Create a vision for change
    d. Communicate the vision
    e. Empower action
    f. Create quick wins
    g. Build on the change
    h. Anchor wins in culture
    
  2. No mechanism to resolve organizational level cross department impediments
        
    a. Impediments not resolved quickly
    b. Resistance is empowered
    c. Teams hit a ceiling and become disenfranchised
    

    Executives: We totally support agile, but what's the date, and what's the features?

  3. Leaders Not Creating Focus

    If the leaders say all projects are equally important - the teams have no priorities.

    So, the people with the least knowledge of corporate urgency, select the tasks that are coolest, easiest, and whose sponsor screams the loudest.

    Impact:
    a. lots of interruptions
    b. No time to improve
    c. no flow
    d. no ability to forecast
    e. slow time to value
    

David showed us how Agile Velocity evaluates how an Agile transformation is going.

Approach Agile by asking why, get buy in, break into smaller things.

Note to Self: How to Use NUnit with C# .Net in Visual Studio for the Mac

I always forget the three libraries to use NUnit with Visual Studio.
Note to self: Download these from NuGet:
  1. NUnit
  2. Microsoft.NET.Test.Sdk
  3. NUnit3TestAdapter

Then select "Run/Run Unit Tests" and VS will create a runner in the bottom pane.


PS: Latest endpoint to connect Visual Studio to the Nuget.org repository is

https://api.nuget.org/v3/index.json

You can put these directly in the csproj:

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
    <PackageReference Include="NUnit" Version="3.11.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
  </ItemGroup>

Friday, April 12, 2019

Smallest example I could think of using C# Async/Await and HttpClient.GetAsync() with Generics

Smallest example I could think of using HttpClient.GetAsync() with Generics.


using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace AsyncHttpClient
{
    /// <summary>
    /// Tiny example of using HttpClient.GetAsync() with Generics.
    /// Uses the REST API calls from the most excellent mysafeinfo.com for presidents and Beatles albums
    /// This prints:
    /// Requesting presidents
    /// Requesting Beatles albums
    /// ... waiting ...
    /// first president = number: '1','', party: '', term: ''
    /// first Beatles album = album name: 'Please Please Me (Mono)', date: '1963-03-22T00:00:00'
    /// </summary>
    public class President
    {
        public int president;
        public string name, party, term;
        public override string ToString() { return $"number: '{president}','{name}', party: '{party}', term: '{term}'"; }
    }
    public class BeatlesAlbum
    {
        public string album, date;
        public override string ToString() { return $"album name: '{album}', date: '{date}'"; }
    }

    class AsyncHttpClientExample
    {
         private static void Main()
        {
            string presidentsUrl = "https://mysafeinfo.com/api/data?list=presidents&format=json";
            string beatlesUrl = "https://mysafeinfo.com/api/data?list=beatlesalbums&format=json&select=ent,typ,rd&alias=ent=artist,typ=album,rd=date";
            var asyncHttpClientExample = new AsyncHttpClientExample();
            Console.Out.WriteLine("Requesting presidents");
            var presidents = asyncHttpClientExample.GetAsync<List<President>>(presidentsUrl);
            Console.Out.WriteLine("Requesting Beatles albums");
            var albums = asyncHttpClientExample.GetAsync<List<BeatlesAlbum>>(beatlesUrl);
            Console.Out.WriteLine("... waiting ...");
            Console.Out.WriteLine("first president = {0}", presidents.Result[0]);
            Console.Out.WriteLine("first Beatles album = {0}", albums.Result[0]);
        }
        private async Task<T> GetAsync<T>(string url)
        {
            HttpClient client = new HttpClient(new HttpClientHandler());
            HttpResponseMessage response = await client.GetAsync(url).ConfigureAwait(false);
            var jsonString = response.Content.ReadAsStringAsync().Result;
            T result = JsonConvert.DeserializeObject<T>(jsonString);

            return result;
        }
    }
}

Thursday, February 21, 2019

Where to Find Your Tesla "Motor Vehicle Purchase Agreement"

So, you just bought a shiny new Tesla, but where is your "Motor Vehicle Purchase Agreement" (aka "Motor Vehicle Purchase Agreement (MVPA)")?

The FAQ  at Tesla is out of date as of Feb 21, 2019.

You can print it from your Tesla account.
1. Login to www.Tesla.com
2. In the top right-hand corner select "TESLA ACCOUNT" (what's with this ALL-CAPS trend?)
3. Select "Manage"
4. Under the car's picture select "View Details".
5. On the right side, near the bottom by "Motor Vehicle Purchase Agreement" select "View".
6. Your pdf of the "Motor Vehicle Purchase Agreement" will be displayed.

By the way, the tax form to get your $7,500 federal tax rebate is 8936.



Thursday, January 31, 2019

Random Notes on C#, Windows, Linux

.Net Core 2.0, to include an HTML fragment into a .cshtml page:

    @await Html.PartialAsync("~/Views/Shared/_Address.cshtml")

To prepare for Dependency Injection in .Net Core 2.0

public void ConfigureServices(IServiceCollection services)
 {
     services.AddSingleton(provider => Configuration);
     services.AddTransient<IUserRepository, UserRepository>();
     services.AddSingleton<IMailer, SmtpMailer>();
 }

How to convert a json string to a C# Object

var myObject= JsonConvert.DeserializeObject(jsonString);

How to create a log4net factory outside of Startup.cs for unit testing.

public static ILoggerFactory GetLoggerFactory()
{
    var loggerFactory = new LoggerFactory();
    loggerFactory.AddProvider(new Log4NetProvider());
    loggerFactory.AddDebug();
    loggerFactory.AddConsole(GetTestConfiguration().GetSection("Logging"));
    loggerFactory.AddLog4Net("log4net.config");

    return loggerFactory;
}

Create simple Moq object in C#

var imailerMoq = new Mock();
imailerMoq.Setup(x => x.SendEmail(It.IsAny(), It.IsAny())).Returns(true);

How to return async task for a unit test

public async Task GetAwesomeString() {
   return await Task.FromResult("I'm the awesome returned text");
}

sudo -s //makes the entire session root

In C# you cannot downcast an object, only cast an object upwards:

wrong    Sheep sheep = (sheep)mammal;
right  Mammal mammal = (Mammal)sheep;

C# -- to get prettyprint of json object with indentation:

var json = JsonConvert.SerializeObject(myObject, Formatting.Indented);

C# to get a key from a value:

Dictionary dictionary = GetMyDictionary();
var name = dictionary.FirstOrDefault(x => x.Value == id).Key;

To change the prompt in linux:

echo "PS1='\w\$ '" >> ~/.bash_profile; source ~/.bash_profile

Wednesday, January 30, 2019

Personal Notes from Udemy Course "ASP.NET Core 2.0"

Here are my notes for my future reference from Jonas Fagerberg's excellent Udemy Course. I would highly recommend you go buy the course: "ASP.NET Core 2.0"

Chapter 3 - MVC

Startup.cs - Let's get wired up!

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AspNetCoreVideoCourse.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace AspNetCoreVideoCourse
{
    public class Startup
    {
        public IConfiguration Configuration { get; set; }
    
        public Startup()
        {
            Configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();
        }
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddSingleton(provider => Configuration);
            services.AddSingleton();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMessageService messageService)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync(messageService.GetMessage());
            });
        }
    }
}

Explicit Attribute Routing

namespace AspNetCoreVideoCourse.Controllers
{
    [Route("[controller]")]
    public class EmployeeController
    {
        [Route("[action]")]
        public string Name()
        {
            return "Jonas";
        }
        [Route("[action]")]
        public string Country()
        {
            return "Sweden";
        }
        [Route("")]
        [Route("[action]")]
        public string Index()
        {
            return "Hello from Employee";
        }
    }
}

Generalized Attribute Routing:

namespace AspNetCoreVideoCourse.Controllers
{
    [Route("company/[controller]/[action]")]
    public class EmployeeController
    {
        public string Name()
        {
            return "Jonas";
        }
        public string Country()
        {
            return "Sweden";
        }
        [Route("")]
        public string Index()
        {
            return "Hello from Employee";
        }
    }
}

To return json data directly to the browser:

public class HomeController : Controller
    {
        public ObjectResult Index()
        {
            var model = new Video {Id = 1, Title = "Shreck"};
            return new ObjectResult(model);
        }
    }

To display an IEnumerable in Razor

@model IEnumerable<AspNetCoreVideoCourse.Models.Video>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Video</title>
</head>
<body>
    <table>
        @foreach (var video in Model)
        {
            <tr>
                <td>@video.Id</td>
                <td>@video.Title</td>
            </tr>
        }
    </table>

</body>
</html>

Chapter 5: Models

Creating an object through POST Example

Data annotations are found in System.ComponentModel.DataAnnotations.

The most common are:
MinLength,MaxLength,Range, RegularExpression, Display, DataType, Required, Compare

Example

in Video.cs
public class Video
{
    public int Id { get; set; }
    [Required, MinLength(3)]
    [DataType(DataType.Password)]
    public string Title { get; set; }
    [Display(Name = "Film Genre")]
    public Genres Genre { get; set; }
}
in ViewModels/ViedoEditViewModel.cs:
namespace AspNetCoreVideoCourse.ViewModels
{
    public class VideoEditViewModel
    {
        public int Id { get; set; }
        [Required, MinLength(3), MaxLength(80)]
        public string Title { get; set; }
        public Genres Genre { get; set; }
    }
}

in HomeController.cs:
HttpGet]
public IActionResult Create()
{
    return View();
}
[HttpPost]
public IActionResult Create(VideoEditViewModel model)
{
    if (ModelState.IsValid)
    {
        var video = new Video
        {
            Title = model.Title,
            Genre = model.Genre
        };
        _videoData.Add(video);

        return RedirectToAction("Details", new {id = video.Id});
    }

    return View();
}
in Views/Home/Create.cshtml:
@using AspNetCoreVideoCourse.Models
@model AspNetCoreVideoCourse.Entities.Video
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<h2>Create Video</h2>
<form asp-action="Create" method="post">
    <div asp-validation-summary="All"></div>
    <table>
        <tr>
            <td><label asp-for="Title"></label></td>
            <td><input asp-for="Title"/></td>
            <td><span asp-validation-for="Title"></span></td>
        </tr>
        <tr>
            <td><label asp-for="Genre"></label></td>
            <td><select asp-for="Genre" asp-items="Html.GetEnumSelectList<Genres>()"></select></td>
            <td><span asp-validation-for="Genre"></span></td>
        </tr>
    </table>
    <input type="submit" value="Create"/>
</form>
<div><a asp-action="Index">Back to List</a></div>

Entity Framework

in package manager:

PM> add-Migration
cmdlet Add-Migration at command pipeline position 1
Supply values for the following parameters:
Name: Initial
PM> update-database

Additional Files:

In Startup.cs:
public class Startup

public IConfiguration Configuration { get; set; }

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: true);

    if (env.IsDevelopment())
    {
        builder.AddUserSecrets<Startup>();

    }

    Configuration = builder.Build();

}
public void ConfigureServices(IServiceCollection services)
{
    var conn = Configuration.GetConnectionString("DefaultConnection");
    services.AddDbContext<VideoDbContext>(options => options.UseSqlServer(conn));
    services.AddMvc();
    services.AddSingleton(provider => Configuration);
    services.AddSingleton<IMessageService, ConfigurationMessageService>();
    services.AddScoped<IVideoData, SqlVideoData>();
}

In SqlVideoData.cs:
:
using System.Collections.Generic;
using AspNetCoreVideoCourse.Data;
using AspNetCoreVideoCourse.Entities;

namespace AspNetCoreVideoCourse.Services
{
    public class SqlVideoData : IVideoData
    {
        private VideoDbContext _db;

        public SqlVideoData(VideoDbContext db)
        {
            _db = db;
        }

        public IEnumerable<Video> GetAll()
        {
            return _db.Videos;
        }

        public Video Get(int id)
        {
            return _db.Find<Video>(id);
        }

        public void Add(Video newVideo)
        {
            _db.Add(newVideo);
            _db.SaveChanges();
        }
    }
}
in VideoDbContext.cs:
using AspNetCoreVideoCourse.Entities;
using Microsoft.EntityFrameworkCore;

namespace AspNetCoreVideoCourse.Data
{
    public class VideoDbContext : DbContext
    {
        public DbSet<Video> Videos { get; set; }

        public VideoDbContext(DbContextOptions<VideoDbContext> options) : base(options)
        {
            
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }
    }
}
       

To Add an "Edit" View

in Edit.cshtml:
<form asp-action="Edit" method="post">
    <div asp-validation-summary="All"></div>
    <table>
        <tr>
            <td><label asp-for="Title"></label></td>
            <td><input asp-for="Title" /></td>
            <td><span asp-validation-for="Title"></span></td>
        </tr>
        <tr>
            <td><label asp-for="Genre"></label></td>
            <td><select asp-for="Genre" asp-items="Html.GetEnumSelectList<Genres>()"></select></td>
            <td><span asp-validation-for="Genre"></span></td>
        </tr>
    </table>
    <input type="submit" value="Edit" />
</form>

in HomeController.cs:
[HttpGet]
public IActionResult Edit(int id)
{
    var video = _videoData.Get(id);
    if (video == null)
    {
        return RedirectToAction("Index");
    }

    return View(video);
}
[HttpPost]
public IActionResult Edit(int id, VideoEditViewModel model)
{
    var video = _videoData.Get(id);
    if (video == null || !ModelState.IsValid) return View(model);

    video.Title = model.Title;
    video.Genre = model.Genre;

    _videoData.Commit();

    return RedirectToAction("Details", new {id = video.Id});
}
in SqlVideoData.cs:
using System.Collections.Generic;
using AspNetCoreVideoCourse.Data;
using AspNetCoreVideoCourse.Entities;

namespace AspNetCoreVideoCourse.Services
{
    public class SqlVideoData : IVideoData
    {
        private VideoDbContext _db;

        public SqlVideoData(VideoDbContext db)
        {
            _db = db;
        }

        public IEnumerable<Video> GetAll()
        {
            return _db.Videos;
        }

        public Video Get(int id)
        {
            return _db.Find<Video>(id);
        }

        public void Add(Video newVideo)
        {
            _db.Add(newVideo);
            _db.SaveChanges();
        }

        public int Commit()
        {
            return _db.SaveChanges();
        }
    }
}

Section 7 Razor Views

_ViewStart.cshtml will define variables for all pages in lower directories
_ViewImports.cshtml will define import statements for all lower pages

Partial Views

  @Html.Partial("_Video", video);
  @await Html.PartialAsync("_Video", video);

Components

Components allow you to inject html from code.

in Views/Shared/Components/Message/Default.cshtml:
using AspNetCoreVideoCourse.Services;
using Microsoft.AspNetCore.Mvc;

namespace AspNetCoreVideoCourse.ViewComponents
{
    public class Message : ViewComponent
    {
        private readonly IMessageService _message;

        public Message(IMessageService message)
        {
            _message = message;
        }

        public IViewComponentResult Invoke()
        {
            var model = _message.GetMessage();
            return View("Default", model);
        }
    }
}
in Views/Shared/_Layout.cs:
<footer>
    @RenderSection("footer",false)
    @await Component.InvokeAsync("Message")
</footer>

Section 8: Forms Authentication / Identity Management

in AccountController:
using System.Threading.Tasks;
using AspNetCoreVideoCourse.Entities;
using AspNetCoreVideoCourse.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace AspNetCoreVideoCourse.Controllers
{
    public class AccountController : Controller
    {
        private readonly UserManager<User> _userManager;
        private readonly SignInManager<User> _signInManager;

        public AccountController(UserManager<User> userManager, SignInManager<User> signInManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        [HttpGet]
        public IActionResult Register()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Register(RegisterViewModel model)
        {
            if (!ModelState.IsValid) return View();

            var user = new User {UserName = model.Username};
            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                await _signInManager.SignInAsync(user, false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError("",error.Description);
                }
            }

            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            return RedirectToAction("Index", "Home");
        }

        [HttpGet]
        public IActionResult Login(string returnUrl = "")
        {
            var model = new LoginViewModel {ReturnUrl = returnUrl};
            return View(model);
        }
        [HttpPost]
        public async Task<IActionResult> Login(LoginViewModel model)
        {
            if (!ModelState.IsValid) return View(model);
            var result =
                await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, false);

            if (result.Succeeded)
            {
                if (!string.IsNullOrEmpty(model.ReturnUrl) &&
                    Url.IsLocalUrl(model.ReturnUrl))
                {
                    return Redirect(model.ReturnUrl);
                }
                else
                {
                    return RedirectToAction("Index", "Home");
                }
            }
            ModelState.AddModelError("","Login failed");
            return View(model);
        }
    }
}  
in Views/Account/Login:
@model LoginViewModel

@{ ViewBag.Title = "Login";}

<h2>Login</h2>

<form method="post" asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">
    <div asp-validation-summary="ModelOnly"></div>
    
    <div>
        <label asp-for="Username"></label>
        <input asp-for="Username"/>
        <span asp-validation-for="Username"></span>
    </div>

    <div>
        <label asp-for="Password"></label>
        <input asp-for="Password"/>
        <span asp-validation-for="Password"></span>
    </div>

    <div>
        <label asp-for="RememberMe"></label>
        <input asp-for="RememberMe"/>
        <span asp-validation-for="RememberMe"></span>
    </div>
    
    <div>
        <input type="submit" value="Register"/>
    </div>

</form>
in Views/Account/Register:
@model RegisterViewModel

@{ ViewBag.Title = "Register";}

<h1>Register</h1>
<form method="post" asp-controller="Account" asp-action="Register">
    <div asp-validation-summary="ModelOnly"></div>
    
    <div>
        <label asp-for="Username"></label>
        <input asp-for="Username"/>
        <span asp-validation-for="Username"></span>
    </div>

   <div>
        <label asp-for="Password"></label>
        <input asp-for="Password"/>
        <span asp-validation-for="Password"></span>
    </div>

   <div>
        <label asp-for="ConfirmPassword"></label>
        <input asp-for="ConfirmPassword"/>
        <span asp-validation-for="ConfirmPassword"></span>
    </div>
    
    <div>
        <input type="submit" value="Register"/>
    </div>
</form>
in ViewModels/LoginViewModel.cs:
using System.ComponentModel.DataAnnotations;

namespace AspNetCoreVideoCourse.ViewModels
{
    public class LoginViewModel
    {
        [Required]
        public string Username { get; set; }
        [Required, DataType(DataType.Password)]
        public string Password { get; set; }
        public string ReturnUrl { get; set; }
        [Display(Name = "Remember Me")]
        public bool RememberMe { get; set; }
    }
}
in ViewModels/RegisterViewModel.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace AspNetCoreVideoCourse.ViewModels
{
    public class RegisterViewModel
    {
        [Required, MaxLength(255)]
        public string Username { get; set; }
        [Required, DataType(DataType.Password)]
        public string Password { get; set; }
        [Required, DataType(DataType.Password), Compare(nameof(Password))]
        public string ConfirmPassword { get; set; }
    }
}
in Views/Shared/_LoginLinks.cshtml:
@using Microsoft.AspNetCore.Identity

@inject SignInManager<User> SignInManager
@inject UserManager<User> UserManger

@if (SignInManager.IsSignedIn(User))
{
    <div>@User.Identity.Name</div>
    <form method="post" asp-controller="Account" asp-action="Logout">
        <input type="submit" value="Logout"/>
    </form>
}
else
{
    <a asp-controller="Account" asp-action="Login">Login</a>
    <a asp-controller="Account" asp-action="Register">Register</a>
}